I'm having some fun with objective c. As a simple program I wanted to write a clock application.
Basically, a UITextField needs to show the current time and update every second.
My initial thought was to use delegation and let UITextField call back a class when the 'Value Changed' event occurs. By 'bootstrapping' an initial value change (e.g. by setting the time at application startup) I thought I could trigger the 'Value Changed' event continuously afterwards (the UITextField would continuously change itself, hence triggering the delegate method). I tried many things, but this never worked. I even tried creating a button that would set UITextField to an arbitrary text value (as opposed to setting UITextField at startup) in the hope that the delegated method would be called, but this did not either. To prove that my code was correct, the time was updated when I'd use other actions like 'Touch Down' for example: I would get the time on every click in the UITextField.
I eventually found out that i could use a callback every second by using [self performSelector ...] and that worked.
Is there a fundamental reason my delegation using the 'Value Changed' action never worked ?
The value changed event only fires in response to a user event-- that is, you setting your textField.text = "something" doesn't fire it, by design.
And it's a good job it doesn't, because by the sounds of it you were trying to get your application into an infinite loop. If the 'value changed' event did actually fire when you set the text in the box, the program would ask the delegate again, which would set the text again, which would ask the delegate again..... you get the picture. This is called an infinite loop, and it has the effect of causing the program to hang, and then crash, since there's no way for the program execution to exit this loop.
Anyway, in order to do what you're saying, you've got two options
you can set up an NSTimer object to call your time update method every second. It's quite easy, check out the documentation.
performSelector:withObject:afterDelay:. It sounds like you might have already got the hang of this one. It's not as neat as using an NSTimer, but it will do the job.
Related
NOTE: Updated below...
I have a cocoa desktop application which consists of a series of controls around a custom NSView. I am using displayLink to drive the updates.
When a user clicks on an NSControl (a slider, a button, a checkbox, a radio button) the application appears to freeze until the mouse is released. I can confirm in fact that the displayLink callback (getFrameForTime) is NOT firing during the time. If I create a timer, that also does not fire, both remain paused until the user releases the mouse, at which point the application resumes updating.
The control is bound, and if I update that value from another thread (for example, via a callback from a MIDI interface) the slider behaves as expected: it moves, the value updates and the application does not pause.
I feel like this should be a fairly obvious fix, but I'm stumped.
Checking "continuous" in IB does as advertised: sends the values continuously, but still exhibits this behavior (preventing the UI update) until the mouse is released.
This seems to be related specifically to mouseDown on NSControl? Why would this block, and do I really need to subclass all my UI elements to change this behavior (seems extreme)
DisplayLink is in its own thread, so why mouseDown on the main thread block it? If this is the case, given the injunction on updating the Cocoa UI from other than the main thread, how do I deal with it?
Any help much appreciated.
Update
Per #Nikolai's comments below, I can confirm that using an NSTimer and adding it to NSEventTrackingRunLoopMode does NOT block. However, I would really like to use CVDisplayLink which (according to the documentation) runs in it's own thread and should not be blocked in this way. Unlike CADisplayLink, I cannot find a way to explicitly assign a runloop to CVDisplayLink (it seems it doesn't work that way), so perhaps the new question should be:
Why does CVDisplayLink block on NSEventTrackingRunLoopMode?
When clicking on an NSControl the runloop mode goes from NSDefaultRunLoopMode to NSEventTrackingRunLoopMode, as long as the mouse is down. That means that only run loop sources (display link) and timers fire that have been added to this mode.
You can add timers to any mode by using -[NSRunLoop addTimer:forMode:]. For a display link the equivalent method is -[CADisplayLink addToRunLoop:forMode:].
To make your animation continue during event tracking you would do something like:
[myDisplayLink addToRunLoop:[NSRunLoop currentRunLoop]
forMode:NSEventTrackingRunLoopMode];
Your test project shows that you are calling a view's display method from within the display link's callback.
When commenting the display message out, the display link is called continuously even while moving the slider.
So what goes wrong is that when the runloop goes into event tracking mode, the call to display on the display link's thread blocks until the mouse is released and the run loop goes back to default mode. You can easily confirm this by putting a log statement before the call and one after it.
Why exactly that happens is not clear to me. What is clear is that it's illegal to call a view's methods from a background thread. You have to trigger the view's display by dispatching a setNeedsDisplay: on the main thread:
static CVReturn MyDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeStamp* now, const CVTimeStamp* outputTime, CVOptionFlags flagsIn, CVOptionFlags* flagsOut, void* displayLinkContext)
{
dispatch_async(dispatch_get_main_queue(), ^{
[(__bridge MyCustomView*)displayLinkContext setNeedsDisplay:YES];
});
return kCVReturnSuccess;
}
How would you go about implementing a "dayDidChange" method?
I have a dateLabel, that needs updating, when the day changes.
I already had 2 solutions implemented. But gave up on them.
1st, on viewWillAppear, I would set my label. Chances for date changing when the user is viewing the VC are VERY small ... and it wouldn't really introduce any "errors" to execution But still, it's not perfect.
2nd, I also implemented an NSTimer with intervals of 1 second. Works great. But I'm not too found of the idea of having a method being called if there is a chance that I don't need to do it.
Are there any other options? I need to update that label, when the day changes. Also, is it possible to use NSNotificationCenter with this?
In your appliciation delegate, implement the following method: -(void)applicationSignificantTimeChange:(UIApplication *)application
This method is called when the day changes, or if the device's time has been changed in the background for whatever reason (such as changes to time zone).
hope it helps. happy coding :)
You can calculate the time left on the day and set a timer to it. You can also listen to UIApplicationSignificantTimeChangeNotification. Note that this notification will be fired not only when the date changes, but for timezone adjustments and other changes. Keep in mind also that the firing of the notification will be triggered only if your app is not in an inactive state.
There are many ways to do so, and I can elaborate more on them if needed.
But here is a nice quick trick
use this code in your app delegate
it will listen to time changes (when the date or time changes dramatically) and when that happens - run a test and update the label if needed.
read me about this here: https://developer.apple.com/library/ios/#documentation/UIKit/Reference/UIApplication_Class/Reference/Reference.html
It's just a quick simple way of doing it without timers.
- (void)applicationSignificantTimeChange:(UIApplication *)application {
NSLog(#"Time has changed, do a test and update the label if needed");
// [yourViewController runTimeTestAndUpdateFunction];
}
I have a situation where my UIPickerView is getting "starved" by a computation task; in other words, the UIPickerView is never updated -- and hence, never sends messages -- because a very heavy compute task is happening. The picker controls aspects of the computation, so the two have to play nice.
I thought of running the computation in a separate thread. Seems like that would leave the picker free to update. However, it'd be a massive undertaking to make my computation multithread-able, so I'd like to find another solution.
Is it possible for a picker (or other UI controls) to "preempt" the execution of a block of code? The computation is in a loop; the number of iterations is what makes it heavy. If the picker could even set a flag somewhere, the loop could break itself, which would work with the flow of the program.
If the loop could poll the picker, that would also work. But, I haven't found a way to do that.
Ideas?
(ps. I posted a similar question yesterday, but didn't really ask it correctly -- didn't quite know what the problem was at that time!)
I assume you mean by flag that if the picker is moved, set a flag. If so you can do this - look at the picker delegates, and when any or all of them get called, set a flag. If your computation is done by another class or classes, then create a new property on those classes "cancelled", the picker can set it, and when set the computation ends.
Before you start another computation you would clear that cancel flag, then kick off the computation.
You can also put a simple toolbar above the picker (common practice) where you have controls that could start the computation, show progress, and cancel it.
EDIT: if the issue is the picker is stuttering when the user is trying to manipulate it, then subclass UIPicker, intercept touch events, and while the picker is being touched, cancel all computations. The only complication is that if the user "spins" the picker, you'd want to wait til it settles, but you would not know how long to wait. Depending on the last touch message, you would have to use a heuristic to wait for didSelectRow: or a timeout before restarting the computation.
I have a tableview with customcells with textfields in it. I am facing a peculiar problem now:
When I tap on first row textfield, -beginEditing gets called.
Now I change the value and tap on second row textfield. So, the -didEndEditing of first row gets called. In this didEnd, I have some parsing methods which are called in some other class. But they are not executed now. Right after the didEnd, -beginEditing of second row text is called. After that the parsing happens. Till now, it is fine.
When the parsing is finished, objects from parsing is set in other classes,the flow should stop here, but I don't know from where and why, The -didEndEditing for the second row gets called ! Also, though any resignfirstresponder is not written anywhere, the keyboard gets dismissed !
Any clue why is this happening and how to solve it ?
This is the way Apple designed the system - all developers have to deal with it (right or wrong). The key is that you are given the "textField" property so you know WHICH one of the textFields is sending the delegate messages.
The solution is to use one or more mutable dictionaries (or some data structure) to keep state for each individual textField. You can have a primary dictionary that uses the textField object as the key, then for each textField a dictionary that has the current state, and any other info you want to retain about it.
You can probably hack a less elegant but easier to code solution to. In any case, there is overlap on these messages and there is not way to avoid it.
EDIT: use the tag as the key, or create a non-retained NSValue pointer object, but don't use the text field itself.
I have encountered a strange bug in my application, and I am trying to debug it using step execution.
However it seems that things on an iphone often do not happen as synchronously as I would like, for example when I step-over this line
[self.view addSubview:FinndomoEmbeddedMWView.view];
nothing happens in the emulator.
If I just let the program run, the view is added and the screen changes as it should.
So I am guessing, addSubview does not do everything related to adding a view, it just sort of starts the process, and then it is completed later.
I don't know if there are message queues on ios similair to winapi, but there must be something like that, so is there a function for "processing all accumulated messages". I would then like to call this function after my addSubview and actually see the things change in the emulator while I debug, not when the program is running.
And I experience this not only with addSubview, so I want to have a general solution for things like this.
Sat yesterday and found out the answer:
NSDate *dtr = [[NSDate alloc] initWithTimeIntervalSinceNow:0.5];
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:dtr];
This is sort of what I wanted. As you can see, this particular code may end up waiting for 0.5 seconds if there are no more events, and if there are events pending, it is only processing the first one and then returns (according to documentation).
But that could be avoided of course, this is just an example, if anyone will want the same thing. In my case there was only 1 important event, and so the provided snippet worked.
The short answer is no.
Cocoa is event-driven.
The core of each app is the event loop. On each pass through the event loop, the app handles events added to the event queue. Updating the screen is one such event.
Thus, changes to screen display don't take place until after your code returns, on the next pass through the event loop.
For debugging purposes where you want to figure out what's happening line-by-line, you need to either use the debugger's facilities, or add NSLog statements to your code.