IOS: Timing issue with hiding images on screen using performSelector - ios

In my ViewController, I attempt to hide two images (currently displayed) at the same time, but after a delay of 3 seconds. I usr
[self performSelector:#selector(hideThem:) withObject:val afterDelay:3.0];
where "hideThem" is a routine that uses the following to hide the images. "val" is simply a NSNumber, not important to this question.
[image1 setHidden:YES];
[image2 setHidden:YES];
If I call "hideThem" directly (not using performSelector), both images disappear at the exact same time, which is the desired affect.
If, I use the performSelector, as shown, one image will hide, then after 0.5 seconds (or so) and the other image will hide. I do not have my own run loop. The images are UIImageView objects and are part of the view hierarchy under "self".
I assume this is an effect with how IOS handles timing of events, but I don't understand why the effect of the setHidden will occur with that 0.5 second delay when both should be set up as hidden "after" the performSelector call to "hideThem" fires.
What about IOS causes this behavior?
What are the recommended approaches to resolve this issue (so that, after 3 seconds, both images are hidden, visually, at the same time.

I can't figure out why such an issue would occur but I can give you an alternative using GCD:
double delayInSeconds = 3.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
[self hideThem];
});

1)
I can see a colon : in method call #selector(hideThem:), double check whether your hideThem method takes input parameters.
2)
Make sure you call this [self performSelector:#selector(hideThem:) withObject:val afterDelay:3.0];
on main thread
try,
performSelectorOnMainThread

Ideas [I thought of 2 first but I suspect 1 will do the trick]:
1) Instead of setting their hidden property, use the UIView animation to turn both of their alphas to 0 over say a 100ms period, and in the completion block, set their hidden property to YES and reset their alphas back to 1.
2) Instead of hiding the two views immediately, create a new view to overlay them. You could even take a "snapshot" of what is under them and create an image out of that first:
create an image that you can use temporarily to overlay the two images you want to hide.
at the appropriate time, add that view to the hierarchy as the topmost view.
hide the two views under it
remove the temporary view

I found the culprit. Thanks for all the suggestions which helped me to isolate the issue. And, David H... very interesting ideas and I may actually have use for them as the project continues.
The answer is: after lots of searching, commenting out code, and tracking through things with logs I finally found ANOTHER related performSelector that was getting called elsewhere, with a timeframe of 2 seconds (hence, what I thought was showing a .5 sec delta between image hidings ).
In short... a strange coding bug.
Thanks again!

Related

iOS - limit on UIView animate?

So I'm making a simple trivia game and I have a timerView that shrinks as time passes. When the user selects an answer, it needs to stop shrinking immediately - it must be very responsive. I give the user 10 seconds per question. Originally I would animate 10 times (with a duration of 1.0f), calling the next "segment" of animation in the completion block of the previous animation. In the completion block I would check to see if the user has tapped an answer, and if so I don't continue the chain. That solution works fine except that it's not very responsive because it's on a per second basis-- user taps an answer at the start of the second segment and the bar has a noticeable continuation.
My solution to THAT problem was to instead have 1000 animation calls with a duration of 0.01f. After doing that, the responsiveness was on point - the view stops animating as soon as I tap an answer -- the issue though, is that it's not actually 10 seconds, it takes more like 20.
So question number 1: what's the smallest time interval animateWithDuration can actually process properly?
Question number 2: is there a better way to accomplish what I'm trying to do accomplish?
ill answer question two: yes there definitely is a better way, have a look at CADisplayLink
use it to shrink your view a little bit each frame, and end the display link when you need to
the most responsive way is: the user taps an answer, you response in the touch callback, remove animations. you can remove animations by CALayer's removeAllAnimations method
Another way to do it is to set the view to shrinking using a single animation with linear timing, and then set the speed of the view's layer to 0 to pause the animation. When you set the speed on the layer to 0 the animation pauses instantly.
This works because under the covers, UIView animation actually creates and installs CAAnimation objects on the view's layers. It's possible to pause and continue an in-flight UIView animation just like you can a CAAnimation.
I have a project called KeyframeViewAnimations (link) on github that allows you to pause, continue, or "scrub" UIView and CAAnimations back and forth with a slider. You could use that technique. The tricky bit will be figuring out how far along the animation is.

How do I hide the launch image without fading it out? [duplicate]

This question already has answers here:
iOS 7 launch image (splash screen) fades out
(7 answers)
Closed 7 years ago.
I want to hide the launch image immediately, instead of fading it out. Is there a way to do this?
You can make an initial view controller with a UIImageView that is identical to your launch image. Then you can specify whatever animation you want between the initial view controller and the first actual view controller in your app.
The answer is fairly straight-forward, as others have said. As long as the UIView behind the launch image is identical in appearance, all you have to do is delay your custom transition by 100 ms (an unnoticeable delay to the user) so that the launch image can fade to 0 alpha.
double delayInSeconds = 0.1;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
dispatch_after(popTime, dispatch_get_main_queue(), ^(){
// Do custom launch image transition
});
This works seamlessly!
Create view controller that contains UIImageView and then just use an NSTimer then change to main controller.
The launch image display time is depended on your app's implement, simpler launch shorter time. I can't tell in the end what factors affect the time. But you can't hide it immediately by yourself, what you can do is just to show some other animations, which must be based on the launch image then do all kinds of launch animations. No matter what kind of animation to make, it will cast more launch time, but make the launch featured.

Set UIView backgroundColor with drawRect:

I am trying to set a custom UIView class's background color. The class also does quartz drawing in the drawRect:method.
Since background color change does not take place until the next redraw of the view, I change the UIView's backgroundColor property before calling setNeedsDisplay. I have set a UIActivityIndicatorView to animate while the view is redrawing.
self.backgroundColor = theColor;
[indicator startAnimating];
[self performSelectorInBackground:#selector(setNeedsDisplay) withObject:nil];
The indicator is stopped at the end of setNeedsDisplay. theColor will change every time I need to call this.
Let's say I have a time consuming setNeedsDisplay process. I would like to set the background and keep the indicator animation. Currently, changing backgroundColor calls setNeedsDisplay but doesn't even change the backgroundColor until the performSelectorInBackground method runs! Therefore my app hangs and no indicator is ever animated.
How do I deal with this ordering problem? Thanks.
Edit: I meant that my drawrect: may be time consuming.
Let's say I have a time consuming setNeedsDisplay process
Let's not. You have no business overriding setNeedsDisplay. I am not at all clear on what you're ultimately trying to accomplish but this entire question seems to be a misunderstanding of how to draw. When you call setNeedsDisplay (which, as you've been told, you must do in the main thread), that's that; you stand out of the way, and when the redraw moment comes, your view's drawRect: is called. That's drawing.
If the problem is simply that the activity indicator never gets going, that's because you never give it a chance. It too is not going to start going until the redraw moment. But you are stopping the activity indicator before the redraw moment even comes! So obviously you'll never see it go.
The way to start an activity indicator visibly before the next thing you do is to step out to the main thread after the next redraw moment. This is called "delayed performance". Example:
self.backgroundColor = theColor;
[indicator startAnimating];
double delayInSeconds = 0.1;
dispatch_time_t popTime =
dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
// do something further, e.g. call setNeedsDisplay
};
You could extend that example by calling dispatch_after yet again to stop the indicator after the next redraw moment.
I must impress upon you, however, that if the mere act of drawing takes so long that you need an activity indicator to cover it, you're drawing wrong. Your act of drawing must be very very fast. You might want to watch the WWDC 2012 video on this very topic; it gives excellent tips on how to draw efficiently.
You can update UI only on Main thread, not in backgroung
Try to use another subview with activity indicator, put in on before redraw and remove from superview after

Detect when UIView/UILabel redraws itself?

I have an app that fetches calendar events and displays data to the user. I'm getting some weird behavior when trying to update my labels.
I can fetch the calendar data just fine but when that gets done, my problem is that according to NSLog my label.text property has already changed, but it's another 4-8 seconds before the view gets redrawn.
Therefore, I'm trying to detect when the label gets redrawn, not when it's .text property changes so I can hide a progress view at the same time the data is populated in the labels.
I have already tried setNeedsDisplay and setNeedsLayout on self.view and the labels themselves. after the .text property of the labels has changed - doesn't work.
So unless I'm completely missing something about using setNeedsDisplay (which I understand only updates on the next redraw anyway), my question is, how do I detect when the UILabel and/or the UIView redraws itself?
How my app is setup:
I've been stuck on this for about 3 weeks.
Make sure setNeedsDisplay is being called on the main thread, using performSelectorOnMainThread:withObject:waitUntilDone:, for example:
[view performSelectorOnMainThread:#selector(setNeedsDisplay)
withObject:nil
waitUntilDone:NO];
Quote apple develop document :
The view is not actually redrawn until the next drawing cycle, at which point all invalidated views are updated.
maybe your main thread are blocking by other things , such as deal with many complex calculations
eg:
- (void)testMethod
{
myLabel.mytext = #"aaaa";
[myLabel setNeedsDisplay];
// some complex calculations
// the quickest , it will run finish the method then redraw.
}

Speed up first UIImageView animation (force cache the images)

My app consists of an animation of about 25 frames. The animations is triggered by a button, and all is working. However, upon first invoking the animation, there is a few second delay.
I am aware that this is because the images need to be cached upon their first run in an animation, and I have ~2mb to be cached.
I wish Apple would implement an [animation prepareToPlay] method for this reason.
In the meantime, I'm running the animation within viewDidLoad. This caches the images during the splash screen, but then displays the animation.
There are two alternatives that I would like to consider.
This first is to forcibly cache the images upon initialization during the splash screen, without the animation playing upon loading the view. (read: custom UIImageView prepareToPlay method) --- This solution would be ideal.
The second alternative would be to keep my current code, running the animation at start-up once the view has been loaded, but disabling the invoking button until this first animation completes. Since UIImageView doesn't have it's own completion notification, would the best solution be to set up a timer equal to the animation duration and enable the button and the end of said timer?
Thanks in advance,
Greg
TL;DR --- How to forcibly cache animation images upon initialization.
I also faced problem as yours. I had created my own question here, and I got good answers. Check it out here: Does UIImageView cache images?
Good luck!

Resources