The title says multiple button presses, but actually I'm looking to stack multiple events. I have a chat application that can receive many chat messages at once. In my messageReceived function, I would like to scroll to the bottom of the most recent chats. The problem I'm having is that sometimes many messages (50+) can come in at the same time. I've determined that scrolling to the bottom is a huge performance bottle neck if performed 50 times, but works great if only performed after messages are received.
I'd like scroll to the bottom after a delay, say 0.1 seconds, in my messageReceived function. But I'd like all new chat messages that occur within this 0.1 seconds to "stack", and only issue a single scroll to bottom request.
I think that a system of setting and canceling timers would work for this, however I can't get it right. Is there a better way to make this happen! thanks!
If you are setting your delayed "scroll to the bottom" request with something like
[[self myObject] performSelector:#selector(scrollToBottom:)
withObject:nil
afterDelay:0.1];
then you can use a cancel command like
[NSObject cancelPreviousPerformRequestsWithTarget:[self myObject]
selector:#selector(scrollToBottom:)
object:nil];
then the only time your scrollToBottom: gets called is when it doesn't get cancelled (so, the last time in the bunch). Now you don't need a timer.
Not an exact solution, but something which could guide to right direction. You can set a flag in your .h file and use that to determine if the method has been already called.
In init method set flag as,
self.didCallScrollToBottomMethod = FALSE;
In your scrollToBottomMethod, check the condition as,
if (!self.didCallScrollToBottomMethod) {
self.didCallScrollToBottomMethod = TRUE;
//set the timer here to scroll after 0.1 seconds
}
In the timer method once the scrolling is completed, set the flag as,
self.didCallScrollToBottomMethod = FALSE;
Related
I have a function that selects a row on a UITableView and then fires didSelectRowAtIndexPath to emulate the row having been clicked. This all happens very fast making it hard to see what has happened, I want to artificially pause between these actions so that it is more pleasing to the eye.
I implemented this using NSTimer using the following code however it only has a resolution as low as 1 second. I would ideally like to pause around 300 ms. How can I achieve as simply as possible?
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1]];
I'm not sure I agree with calling didSelectRowAtIndexPath directly. I would suggest moving whatever you're doing in there to a separate method (say, selectionResponse:) and calling that directly.
Once you've done this, you can use performSelector:withObject:afterDelay: to call your selectionResponse: method, setting the afterDelay: argument to whatever value you want.
The link for the documentation on performSelector:withObject:afterDelay is here
Note that it's also a good idea to use + cancelPreviousPerformRequestsWithTarget:selector:object: (from the same document) in your dealloc, to cancel a pending perform request if your user chooses to back out of your view controller within the delay period, before the selector is invoked. This will prevent a crash.
Is there any significant difference in performance when you call
[someObject performSelector:#selector(testMethod:) withObject:anotherObject];
vs
[someObject testMethod:anotherObject];
?
The first causes an extra call to objc_msgSend() that isn't necessary in the second case.
The performance difference is unlikely to remotely matter unless you are calling said method as quickly as you possibly can many 10s of thousands of times and you aren't doing any significant work in testMethod:.
I.e. don't worry about it unless you measure an actual performance problem.
Interesting fact, performing a selector with a delay of 0 causes that method to be called at the top of the next run loop of the app. You can use that to delay certain events that occur frequently (used a lot in optimizations of UI, like images that get reloaded in a UIScrollView)
No there isn't any performance hit that I am aware of, and if there is any it is not significant.
I’ve come across an important difference when passing data to another view controller in prepareForSegue.
using:
[viewController performSelector:#selector(aMethod:) withObject:anObject];
aMethod is called AFTER viewDidLoad and viewWillAppear of the destination viewController.
using:
[viewController aMethod: anObject] ;
aMethod is called BEFORE viewDidLoad and viewWillAppear of the destination viewController.
So if you’re sending data important for the setup of the destination viewController, use the second way.
There is a lot difference in above both methods. I was trying to get animation of Two buttons coming from right side and stops at center but the second button was coming with 0.3 second delay. Now the main point comes here. I was using one animation method for both of 2 buttons. Now i wanted that when I click Finish button, then both buttons should go to left and again New buttons come. This was fine till reading.
Now when i was writing method for Finish button click. I was performing going out of buttons Animation first and then coming in buttons, but when I used the Above second method i.e. [someObject testMethod:anotherObject]; then what happens is I was not able to see the Going out Animation and directly coming in animation of buttons was shown.
Here actually comes the use of first method i.e. [someObject performSelector:#selector(testMethod:) withObject:anotherObject withDelay:delay];
The reason I found was when I click the Finish button the animation runs in different thread and the other code runs in different thread so the going out action was performed in another thread and coming in was performed in another thread. So first thread was not shown.
After using first method with Delay time of total animation. I achieved my goal. So both methods have their own significance.
For my experience,there are two differences:
The first one can add afterDelay:(CGFloat)seconds, and this is the only case I use the first one.
[someObject performSelector:#selector(testMethod:) withObject:anotherObject afterDelay:1.0];
The second one, you need to define it in someObject.h. Otherwise, you will get a compile warning.
The answer is that they are exactly the same.
There are two really good articles one from Mike Ash, where he explains the objc_msgSend():
http://www.mikeash.com/pyblog/friday-qa-2012-11-16-lets-build-objc_msgsend.html
And an another one from Tom Dalling who is explaining that perform selector is calling objc_msgSend().
http://tomdalling.com/blog/cocoa/why-performselector-is-more-dangerous-than-i-thought/
My app is playing a pretty complex animation. It's like a flipbook.
What I do is: I have a huge loop with selectors, and after every delayed call the next one is called.
Now someone calls the user and the device suddenly shows up this fat green status bar and maybe some big pick-up-the-phone-call overlay. Or: The alarm clock rings, and a big alert sheet appears in front of just about everything.
It would be great to just pause the whole animation in case of ANY interruption. Probably I've also missed like 5 more possible interruptions.
How are you doing that? How do you get notified for all interruptions and then call one single -stopEverything method?
Whenever the app becomes inactive, the UIApplicationWillResignActiveNotification local notification will be posted. In the opposite suituation, the UIApplicationDidBecomeActiveNotification notification will be posted.
Your animation logic can listen to this and respond appropriately. There is an +setAnimationsEnabled: method to kill all current and future animations, but there isn't a documented "global pause" method.
Depending on the animation, you might be better off using CoreAnimation directly by using a single CAKeyframeAnimation on the view's -layer rather than having one animation's completion selector start another animation.
I am working on Iphone application and new to dev. I am sorry if my question is very basic one. I searched net, but could not get answer.
My question is, when user touches the iphone, I want to get that event to be exexecuted immediately instead of waiting for the previous event to complete.
I call while loop(present in different class) inside touchBegan method. I want to stop that loop when I get another touch event. But touchBegan event is not called (instead queued) as I am still present in the touch began.
Can anybody please help me how to fire the touch event immediately instead of waiting for the previous event to finish.
Thank you in advance.
On the iPhone, one event has to run to completion before the next event is processed. Basically you want to have code which does not run for a long time so that your app remains responsive to user input.
There are a number of way you can handle this.
Put some of the code in a new thread and start that thread from your touchBegan method.
Or turn the loop into multiple events on the main thread. You can do this by calling:
- (void)initializeLoop
{
loopCounter = 0;
[self performSelector:#selector(nextLoop) withObject:nil afterDelay:0.0];
}
- (void)nextLoop
{
loopCounter++;
if (loopCounter<MaxCount)
[self performSelector:#selector(nextLoop) withObject:nil afterDelay:0.0];
}
That way the new touchEnded event can be handled between these other events. Setting the delay higher than 0.0 will make the app even more responsive.
I've implemented a tap-and-hold handler using an NSTimer that I first set in the TouchesBegan overload.
However, what I actually want is for an action to be continuously performed in quick-fire succession while the touch is being held. So, on timer expiry I call a handler to do the work, which then sets another timer and the cycle continues until the TouchesEnded comes in and cancels it, or another terminating condition is met.
This works fine, until my handler code triggers an animation to go off at the same time.
Now we have animation events and timer events going off, and in all that we need to handle TouchesEnded as well.
What I am finding is that, if the animation is triggered, and I set my timer to less than 0.025 seconds, my TouchesEnded event doesn't come through until the timer cycle stops (the other terminating condition). Setting a slower timer, or not triggering the animation, make it work (TouchedEnded comes in straight away), but are not what I want.
Obviously this is all on the device (release build - no NSLogs) - in the sim it all works fine
Is there any way of setting the relative priorty of these events - or is it likely I'm missing something else obvious here?
[Update]
I've worked around this in this instance by doing the continuous part without visual feedback until it's done (which from this users perspective is instant). I think this is ok for now. I'd still like to hear any more thoughts on this (Jeffrey's idea was good), but I'm not waiting on tenterhooks now.
Try writing your own Timer-type class by spawning off onto a thread. Example:
BOOL continue = YES; //outside of your #implementation
-(void)doLoop
{
while(continue){
[NSThread sleepForTimeInterval:.025];
[self performSelectorOnMainThread:#selector(whateverTheFunctionIs) waitUntilDone:YES];
}
}
and this would be started by [NSThread detatchNewThreadSelector:#selector(doLoop) toTarget:self withObject:nil]. This is not exactly threadsafe, but you can choose to wrap the boolean into a NSNumber and then do #synchronize on it if you so choose. Alternatively, after I wrote that little snippet I realized it would be better to do a check against the current NSTime instead of sleepForTimeInterval: but you get the point. :)