I am testing a basic animation using a while loop. When the user touches a start button, an image randomly appears with every click of the button. I then placed the same code within a while loop and the image does not seem to move. I am thinking that it is just moving too fast through the iterations and therefore appears to not be moving. So, is there a way, simple or otherwise, that I can delay the speed of the loop so I can see a transition to some degree? I'm thinking that the speed of the loop is what is doing this, as text is instantly outputted to the label, thus I know the loop is working at least. Here is what I have...
#IBAction func btnStart(sender: AnyObject) {
isMoving = true
var i = 0
while (i != 200) //arbitrary number of iterations chosen for this example
{
var x = arc4random_uniform(400)
var y = arc4random_uniform(400)
myButton.center = CGPointMake(CGFloat(x), CGFloat(y));
++i
lblHitAmount.text = String(i) //200 appears instantaneously in label, loop is working
}
}
EDIT:
var timer = NSTimer()
var counter = 0
timer = NSTimer.scheduledTimerWithTimeInterval(0.5, target: self, selector: Selector("tolabel"), userInfo: nil, repeats: true)
var i = 0
while (i != 800)
{
var x = arc4random_uniform(400)
var y = arc4random_uniform(400)
btnBug.center = CGPointMake(CGFloat(x), CGFloat(y));
++counter
++i
//lblHitAmount.text = String(i)
}
func tolabel(){
lblHitAmount.text = String(counter++)
}
So to understand why you are getting this behavior, you need to understand the idea of the run loop. Every app has a run loop that is essentially an infinite loop. Each iteration of the loop, it does a number of things. Two of the primary things are handling events (like tapping a button) and updating the GUI. The GUI is updated only once per run loop.
Your event handling code (for tapping btnStart), is run inside a single run loop iteration. No matter how long you delay your loop (say 10 minutes), the GUI will remain frozen until it finishes and then only display the last value the text was set to. This is why you get freezes in apps and programs. They have something running inside a single iteration of the run loop not allowing the execution to return to updating the GUI.
If you want to update the text periodically, you should look into NSTimer. You can setup a timer that repeats every X seconds and each time the timer is fired it will be executed in a different iteration of the run loop allowing the GUI to be updated with the intermediate values:
#IBAction func btnStart(sender: AnyObject) {
NSTimer.scheduleTimerWithTimeInterval(0.5, target: self, selector: "updateText:", userInfo: nil, repeats: false)
}
func updateText(timer: NSTimer) {
// do something to update lblHitAmount.text
// if you want to execute this again run:
NSTimer.scheduleTimerWithTimeInterval(0.5, target: self, selector: "updateText:", userInfo: nil, repeats: false)
// if you are done with the "loop" don't call scheduleTimerWithTimeInterval again.
}
Related
I am trying to make a for loop stop when a string contains a certain numeral. The only problem is it stops adding value to the string after adding 31 to the counter. I feel it is going to fast before it can play catch up. I know there's always another way to complete the task but I would just want to understand why it does this.
What I'm trying to accomplish is having the counter act as the height. The theSlice = 8 and webSlices is a list. The whole text says "Height is a 1" so I am trying to see what number is the height and make counter equal that.
Code:
func breakDownSlice(theSlice int, webSlices []string) {
counter := 0
done := false
for done == false {
log.Println(webSlices[theSlice])
log.Println(counter)
done = strings.Contains(webSlices[theSlice], string(counter))
if done == true {
log.Printf("The height is %d", counter)
} else {
counter++
}
}
}
the line
done = strings.Contains(webSlices[theSlice], string(counter))
string(counter) can't work here, use strconv.Itoa(counter) instead
I want to operate my window coverings with my smartphone. Now every time I change the position on my phone a method
func main() {
OnUpdate(func(tPos int) {
wc(tPos,cPos)
cPos = tPos
}
}
is called where tPos is an integer between 0 and 100 which is is target position. There also is a variable for the current position cPos. OnUpdate should call a function which either open or closes the window covering depending of the order relation between cPos and tPos. This function looks like this.
func wc(tPos int, cPos int){
switch{
case tPos == 0:
log.Println("close")
case tPos == 100:
log.Println("open")
case tPos > cPos:
t := time.Duration( (tPos - cPos)*10*openTime)
log.Println("open")
time.Sleep( t * time.Millisecond)
log.Println("stop")
case tPos < cPos:
t := time.Duration( (cPos - tPos)*10*closeTime)
log.Println("close")
time.Sleep( t * time.Millisecond)
log.Println("stop")
}
}
My problem now is that there should be some delay. I want that after OnUpdate is called there is a timer for like 3 seconds and then wc is called unless OnUpdate is called again during that 3 seconds.
But I don't know how to do this. Can someone tell me what is a good way to do that?
Not completely sure about your meaning, but I'll give it a go anyway. What calls OnUpdate? Is that on you phone? Do you simply want to sleep before calling wc inside the callback you provide to OnUpdate?
With that in mind please have a look at : https://play.golang.org/p/4vVpEEUcqg
My understanding is that you want make sure wc is not called too often. The for/select statements in run makes sure that wc is only called once every 3 seconds at most.
Here's some sample code that puts a Gauge on the screen and make the progress bar increase 1 value every second. On MacOS I don't see the progress bar update unless I drag the window around or resize it manually with the mouse. Any idea how to force the whole thing to repaint? I'm calling f.Refresh() and f.Update()
package main
import "github.com/dontpanic92/wxGo/wx"
import "time"
var g wx.Gauge
type MyFrame struct {
wx.Frame
}
func (f *MyFrame) startUpload() {
for {
time.Sleep(time.Second)
g.SetValue(g.GetValue() + 1)
f.Refresh()
f.Update()
}
}
func NewMyFrame() MyFrame {
f := MyFrame{}
f.Frame = wx.NewFrame(wx.NullWindow, -1, "Test Thread")
mainSizer := wx.NewBoxSizer(wx.HORIZONTAL)
g = wx.NewGauge(f, wx.ID_ANY, 100, wx.DefaultPosition, wx.NewSize(600, 40), 0)
f.SetSizer(mainSizer)
mainSizer.Add(g, 100, wx.ALL|wx.EXPAND, 50)
f.Layout()
go f.startUpload()
return f
}
func main() {
wx1 := wx.NewApp()
f := NewMyFrame()
f.Show()
wx1.MainLoop()
return
}
Update: I've been reading http://docs.wxwidgets.org/trunk/overview_thread.html and I'm trying code like:
b := wx.NewPaintEvent()
f.GetEventHandler().QueueEvent(b)
instead of calling Refresh and Update but my wx.NewPaintEvent doesn't do anything. Maybe I'm making the wx.NewPaintEvent wrong? Or I'm adding it to the wrong EventHandler?
Generally speaking, doing anything with GUI objects from the non-main (i.e. not the one that initialized the library) thread is not supported by wxWidgets.
The usual workaround is to post an event to the main thread asking it to update the widget instead of doing it directly in the worker thread. In C++ this can be done easily and elegantly using CallAfter(), but I don't know enough about Go and wxGo to show you an example of doing it in this language.
author of wxGo fixed it:
https://github.com/dontpanic92/wxGo/issues/10
wx.Bind(f, wx.EVT_THREAD, func(e wx.Event) {
threadEvent := wx.ToThreadEvent(e)
the_gauge.SetValue(threadEvent.GetInt())
}, UPLOAD_WORKER_ID)
then in the thread:
threadEvent := wx.NewThreadEvent(wx.EVT_THREAD, UPLOAD_WORKER_ID)
threadEvent.SetInt(50)
f.QueueEvent(threadEvent)
I created a simple action sequence that will perform a few tasks on a sprite object, but i would also like to make a custom action within the sequence that will print something for a certain number a seconds seconds at specific time intervals intervals (e.g print "hello world" for 10 seconds at 1 second intervals). I tried creating a method that returns an skaction, but that didnt work, and the action ran at when the sequence was first called instead of after the preceding action finished. Here's what i got so far
let printAction = SKAction.customActionWithDuration(10) { (SKNode, CGFloat) -> Void in
NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: Selector("printit"), userInfo: nil, repeats: false)
}
sprite.runAction(SKAction.repeatActionForever(SKAction.sequence([printAction,scale,printAction])))
func printit() {
print("hello world")
}
You are combining two approaches that can solve your problem individually but the combination you're making will not work. (you are triggering multiple 1 second timers from an action that will call your capture block an unpredictable number of times at an uncontrollable speed).
Solution using SKAction:
let printMessage = SKAction.runBlock(){ self.printIt() }
let wait = SKAction.waitwaitForDuration(1)
let printCycle = SKAction.sequence([printMessage, wait])
self.runAction(SKAction.repeatAction(printCycle, count:10))
func printIt()
{
print("hello world")
}
solution using NSTimer:
for time in 0..<10
{
NSTimer.scheduledTimerWithTimeInterval(NSTimeInterval(Double(time)),
target: self, selector: Selector("printIt:"),
userInfo: nil, repeats: false)
}
func printIt(_ : NSTimer)
{
print("hello world")
}
In a Sprite Kit environment, I would recommend that you don't mix frameworks and go with the SKAction approach. It will make it easier to control/clean-up the game environment by linking everything to your SKScene and/or SKView
I have a slider and I want to limit its value to range from 1 to 5. I have set the min and max values on both Interface Builder and in codes but am currently facing two main issues.
1) The value doesn't change (keeps saying value 1).
2) On top of that, I am also unable to adjust the value of the slider when VoiceOver accessibility mode is on. The value keeps reading it as 1, even though on the surface, it may be on value 3/4/5.
Here are some of my codes to implement the slider in Swift. I am not sure what is the problem here with this. Any help is appreciated!
#IBOutlet weak var busSlider: UISlider!
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
busSlider.minimumValue = 1
busSlider.maximumValue = 5
}
override func viewDidAppear(animated: Bool) {
busSlider.accessibilityValue = NSNumberFormatter.localizedStringFromNumber(busSlider.value, numberStyle: NSNumberFormatterStyle.DecimalStyle)
busSlider.accessibilityLabel = NSLocalizedString("\(Int(busSlider.value))", comment: "")
}
override func accessibilityIncrement() {
busSlider.value++
busSlider.sendActionsForControlEvents(UIControlEvents.ValueChanged)
busSlider.accessibilityValue = NSNumberFormatter.localizedStringFromNumber(busSlider.value, numberStyle: NSNumberFormatterStyle.DecimalStyle)
busSlider.accessibilityLabel = NSLocalizedString("\(Int(busSlider.value))", comment: "")
}
override func accessibilityDecrement() {
busSlider.value--
busSlider.sendActionsForControlEvents(UIControlEvents.ValueChanged)
busSlider.accessibilityValue = NSNumberFormatter.localizedStringFromNumber(busSlider.value, numberStyle: NSNumberFormatterStyle.DecimalStyle)
busSlider.accessibilityLabel = NSLocalizedString("\(Int(busSlider.value))", comment: "")
}
*On the side note: I actually have 2 sliders on the same screen but belonging to different sections. The first slider reads the value in percentage (speak rate), and this slider needs to read the value in integer (number of stops).