Making the app wait a few seconds before executing code? - ios

Im trying to implement a way of taking a screenshot in my application. I want the UINavigationBar tip slide up - take the screenshot - and then the UINavigationBar can slide down nice and easy. I need the app to wait/hold a few seconds between some lines of code, because this way the first animation does not get time to finish:
[self.navigationController setNavigationBarHidden:YES animated:YES ];
[self.navigationController setNavigationBarHidden:NO animated:YES];
So, is there a way of delaying execution, like when animation a button like so:
[UIView animateWithDuration:0.5 delay:3 options:UIViewAnimationOptionCurveEaseOut animations:^{self.myButton.frame = someButtonFrane;} completion:nil];
regards

You can use:
double delayInSeconds = 2.0; // number of seconds to wait
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
/***********************
* Your code goes here *
***********************/
});

You can use:
[self performSelector:#selector(hideShowBarButton) withObject:nil afterDelay:1.0];
and of course:
- (void) hideShowBarButton{
if (self.navigationController.navigationBarHidden)
[self.navigationController setNavigationBarHidden:NO animated:YES ];
else
[self.navigationController setNavigationBarHidden:YES animated:YES ];
}

While there doesn't appear to be a callback for setNavigationBarHidden's completion, it will take UINavigationControllerHideShowBarDuration seconds. So just use a NSTimer to delay it:
[NSTimer scheduledTimerWithTimeInterval:UINavigationControllerHideShowBarDuration target:self selector:#selector(myFunction:) userInfo:nil repeats:NO];
You may want to add a small amount to the delay as a fail-safe;
[NSTimer scheduledTimerWithTimeInterval:UINavigationControllerHideShowBarDuration+0.05 target:self selector:#selector(myFunction:) userInfo:nil repeats:NO];
See also this related question: UINavigationControoller - setNavigationBarHidden:animated: How to sync other animations

Related

Hide a UIlabel after some seconds

I'm using this following code to hide a UILabel after some seconds. Unfortunately if the user close the view during the NSInvocation is in progress the app crashes
- (void)showStatusBarwithText:(NSString*)text{
lblNotification.hidden=NO;
NSInvocation* invoc = [NSInvocation invocationWithMethodSignature:[lblNotification methodSignatureForSelector:#selector(setHidden:)]];
[invoc setTarget:lblNotification];
[invoc setSelector:#selector(setHidden:)];
lblNotification.text=text;
BOOL yes = YES;
[invoc setArgument:&yes atIndex:2];
[invoc performSelector:#selector(invoke) withObject:nil afterDelay:1];
}
and that's the error
*** -[UILabel setHidden:]: message sent to deallocated instance 0x1a8106d0
How can I solve? I have tried using
[NSObject cancelPreviousPerformRequestsWithTarget:lblNotification]
In the - (void)viewDidDisappear:(BOOL)animated but it doesn't work.
Here is how you should use performSelector
- (void)showStatusBarwithText:(NSString*)text{
lblNotification.hidden=NO;
[self performSelector:#selector(hideLabel) withObject:nil afterDelay:1];//1sec
}
-(void)hideLabel{
lblNotification.hidden= YES;
}
or with the timer
[NSTimer scheduledTimerWithTimeInterval:1//1sec
target:self
selector:#selector(hideLabel)
userInfo:nil
repeats:NO];
Why don't you just use dispatch_afer? The syntax is much more clear:
- (void)showStatusBarwithText:(NSString*)text{
lblNotification.hidden=NO;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
lblNotification.hidden = YES;
});
}
It's because you pass lblNotification instead of infoc object here:
[NSObject cancelPreviousPerformRequestsWithTarget:lblNotification]
It will be better to do this way:
- (void)showStatusBarwithText:(NSString*)text{
lblNotification.hidden=NO;
lblNotification.text=text;
[lblNotification performSelector:#selector(setHidden:) withObject:#(1) afterDelay:2];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated]
[NSObject cancelPreviousPerformRequestsWithTarget:lblNotification];
}

UIView animation not working in iOS 7

I am working on a sample project in which I have a vertical scrollview and a horizontal scrollview. The vertical scrollview has a number of subviews. In scrollviewDidScroll I am performing some actions. Along with this I now want to animate a subview inside the vertical scrollview when that particular subview is visible on the screen. For this I am doing a certain calculation which is correct. The animation is as follows:
The subview contains multiple custom views. I am trying to animate(reducing and then again increasing alpha value for the view) each of these views in some particular time sequence(so that the animation looks to be in sequence). For this I am posting notification to the views and the animation sequence and logic is perfect according to what I want.
But I am facing the problem that the code is executing when I put the breakpoint, but the animation does not show up. In case if I post the notification after I stop scrolling then the animation happens perfectly fine. But I want is the animation to happen even when I am scrolling and the view is on screen.
I am adding my code snippet as below:
SubView: (Inside my scrollview)
- (void)animateSequenceTemplate {
if (!hasSequenceTemplateAnimated) {
[self performSelector:#selector(animateSequenceSlides) withObject:nil afterDelay:0];
hasSequenceTemplateAnimated = YES;
}
else {
return;
}
}
- (void)animateSequenceSlides {
NSDictionary *userInfo = [NSDictionary dictionaryWithObject:imageAsset forKey:#"assetMO"];
[[NSNotificationCenter defaultCenter]postNotificationName:AnimateSequenceSlide object:nil userInfo:userInfo];
}
Subviews inside the above subview:
- (void)animateSlideView:(NSNotification *)notification {
NSDictionary *userInfo = notification.userInfo;
if ([userInfo objectForKey:#"assetMO"] == assetMO) {
[[NSNotificationCenter defaultCenter]removeObserver:self name:AnimateSequenceSlide object:nil];
CGFloat duration = 0.035;
float delay = (self.slideCount * duration);
CGFloat timeDelay = 0;
[self performSelector:#selector(animationPhase1) withObject:nil afterDelay:delay];
timeDelay = delay + duration;
[self performSelector:#selector(animationPhase2) withObject:nil afterDelay:timeDelay];
if (self.slideCount == (self.maxSlideCount - 1)) {
timeDelay += duration * 18; //No animation till 18 frames
}
else {
timeDelay += duration;
}
[self performSelector:#selector(animationPhase3) withObject:nil afterDelay:timeDelay];
timeDelay += duration;
[self performSelector:#selector(animationPhase4) withObject:nil afterDelay:timeDelay];
}
}
- (void)animationPhase1 {
[UIView animateWithDuration:0.035 delay:0.0 options:UIViewAnimationOptionBeginFromCurrentState animations:^ {
[self setAlpha:0.85];
}completion:^(BOOL finished) {
}];
}
There occurs two scenarios inside - (void)animateSequenceTemplate:
I call as [self animateSequenceSlides];. In this case the animation does not show up.
[self performSelector:#selector(animateSequenceSlides) withObject:nil afterDelay:0.0f];. In this case the animation shows up but after the scrollview rests.
I am compelled to use the UIView animation using perform selector because if I remove this and use either nested UIView animate blocks / or directly call these methods, then again animation does not show up. Now at lease it shows up after the I rest scrolling.
I would like advice on a solution or any guess on what mistake I might be making.
It possible, that you executing it not on the main queue.
dispatch_async(dispatch_get_main_queue, block()) might help.
When working with animation, main queue the only place for executing code, that makes animation happen.
EDIT:
[self performSelector:#selector(animationPhase1) withObject:nil afterDelay:delay];
I’m not sure, where it happens, but I don't think on main thread.
Try this:
[self performSelectorOnMainThread:#selector(animationPhase1)
withObject:nil
waitUntilDone:YES];
I’m not sure about delay, that’s why I prefer GCD functions and blocks.
Finally, i got the solution to my question and it is working very fine as expected.
Changes Made:
From "animateSequenceTemplate", calling "animateSequenceSlides"
method directly
In "animateSlideView" method mentioned above, instead of using perform selector I created NSTimer object with the required delay,
repeat set to NO, and adding the timer in the current run loop
Did the same for calling all the four animationPhase methods
Code works fine as accepted
- (void)animateSequenceTemplate {
if (!hasSequenceTemplateAnimated && self.isSequenceSlideCreated) {
hasSequenceTemplateAnimated = YES;
[self animateSequenceSlides];
}
}
- (void)animateSlideView:(NSNotification *)notification {
NSDictionary *userInfo = notification.userInfo;
if ([userInfo objectForKey:#"assetMO"] == assetMO) {
[[NSNotificationCenter defaultCenter]removeObserver:self name:AnimateSequenceSlide object:nil];
CGFloat duration = 0.035;
float delay = (self.slideCount * duration);
CGFloat timeDelay = 0;
NSTimer *animate1 = [NSTimer scheduledTimerWithTimeInterval:delay target:self selector:#selector(animationPhase1) userInfo:nil repeats:NO];
[[NSRunLoop currentRunLoop]addTimer:animate1 forMode:NSRunLoopCommonModes];
timeDelay = delay + duration;
NSTimer *animate2 = [NSTimer scheduledTimerWithTimeInterval:timeDelay target:self selector:#selector(animationPhase2) userInfo:nil repeats:NO];
[[NSRunLoop currentRunLoop]addTimer:animate2 forMode:NSRunLoopCommonModes];
if (self.slideCount == (self.maxSlideCount - 1)) {
timeDelay += duration * 18; //No animation till 18 frames
}
else {
timeDelay += duration;
}
NSTimer *animate3 = [NSTimer scheduledTimerWithTimeInterval:timeDelay target:self selector:#selector(animationPhase3) userInfo:nil repeats:NO];
[[NSRunLoop currentRunLoop]addTimer:animate3 forMode:NSRunLoopCommonModes];
timeDelay += duration;
NSTimer *animate4 = [NSTimer scheduledTimerWithTimeInterval:timeDelay target:self selector:#selector(animationPhase4) userInfo:nil repeats:NO];
[[NSRunLoop currentRunLoop]addTimer:animate4 forMode:NSRunLoopCommonModes];
}
}
I am not satisfied with the goodness of the code structure. If you think of any other smart way of doing it, please share.

Timer to show/ hide subview

I am stuck in something I hope you guys can help
I have a scrollview , when the user scrolling a subview appear with animation from bottom to top. the timer then start counting 5 sec and then call another method to hide the subview
I implemented and it works as wanted except :
while the subview appear and when it's almost to hide , if I scrolled that moment the subview appear statically and never hide . try to scrolling again another subview dynamically work over the static one ( as it duplicated or something)
this is my code for controlling show and hide of subview
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
if(!show){
[self showSubview];
if (!myidleTimer)
[self resetIdleTimer];
}
}
-(void)resetIdleTimer
{
//convert the wait period into minutes rather than seconds
int timeout = kApplicationTimeoutInMinutes;// equal to 5 seconds
[myidleTimer invalidate];
myidleTimer = [NSTimer scheduledTimerWithTimeInterval:timeout target:self selector:#selector(idleTimerExceeded) userInfo:nil repeats:NO];
}
-(void)idleTimerExceeded
{
if (show){
[myidleTimer invalidate];
[self hideSubview];
show=false;
}
}
"show" is a bool to insure when to hide and when to show
her is the show / hide implementation
-(void)hideSubview{
[UIView animateWithDuration:0.5
animations:^{
subview.frame = CGRectMake(0, screenWidth, screenHeight, 60);//move it out of screen
} completion:^(BOOL finished) {
[subview removeFromSuperview];
subview.frame=CGRectMake(0,screenWidth, screenHeight, 0);
}];
show=false;
}
-(void) showSubview{
subview = [[UIView alloc] init ];
[self.view addSubview:subview];
subview.frame = CGRectMake(0, screenWidth, screenHeight, 60);
[UIView animateWithDuration:1.0
animations:^{
subview.frame = CGRectMake(0, screenWidth-60, screenHeight, 60);
}];
show=TRUE;
}
I hope that's clear enough to understand and be able to help me identify the problem
thanks in advance
The timer will not fire while the scroll view is being scrolled, if you create the timer the way you do. Instead, create it as below.
NSTimer *timer = [NSTimer timerWithTimeInterval:1 target:self selector:#selector(doStuff:) userInfo:nil repeats:YES];
[[NSRunLoop mainRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
scheduledTimerWithTimeInterval:target:selector:userInfo:repeats: adds the timer to the run loop using the defaultRunLoopMode instead of the NSRunLoopCommonModes, which is the one you want to have timer fire while the user scrolls.

Bounce UIScrollView - hint that there's more

I'm developing an app with a scroll view in it. It's not immediately obvious that there's more content, and there's no scroll indicator (scroll view is paged).
So, to give the user a 'hey, there's something down here...', I would like to have the scroll view do a subtle bounce - down then up - on launch. I've tried this:
- (void)viewDidLoad
....
[NSTimer scheduledTimerWithTimeInterval:0.8 target:self selector:#selector(bounceScrollView) userInfo:nil repeats:NO];
}
- (void)bounceScrollView
{
[self.verticalScrollViews[0] scrollRectToVisible:CGRectMake(0, 600, 1, 1) animated:YES];
[NSTimer scheduledTimerWithTimeInterval:0.01 target:self selector:#selector(_unbounceScrollView) userInfo:nil repeats:NO];
}
- (void)_unbounceScrollView
{
[self.verticalScrollViews[0] scrollRectToVisible:CGRectZero animated:YES];
}
However, this code makes the view get 'stuck' at about halfway between two pages.
Any help?
Idea 1: You need to turn off paging, animate the bounce, and turn the paging back on.
Idea 2: Your second move is coming way too soon:
scheduledTimerWithTimeInterval:0.01
Experiment with longer time intervals! I would start with 0.4.
Idea 3: Instead of your bounce, why not use flashScrollIndicators? This is exactly what it is for!
I had a memory problems when using NSTimer. That's why I used another solution for scrollView preview.
[UIView animateWithDuration:1.0
delay:0.00
options:UIViewAnimationOptionCurveEaseInOut
animations:^{
_scrollView.contentOffset = CGPointMake(200,0);
}
completion:^(BOOL finished){
[UIView animateWithDuration:1.0
delay:0.00
options:UIViewAnimationOptionCurveEaseInOut
animations:^{
_scrollView.contentOffset = CGPointMake(0,0);
}
completion:^(BOOL finished){
}
];
}
];

ios animation one by one

I have some views and I want to display them one by one . I can't find how to do this . Instead , if I use the following code , all the views appear at once .
NSTimer *timer1 = [NSTimer scheduledTimerWithTimeInterval:2 target:self selector:#selector(showStar2) userInfo:nil repeats:NO];
NSTimer *timer2 = [NSTimer scheduledTimerWithTimeInterval:2 target:self selector:#selector(showStar3) userInfo:nil repeats:NO];
NSTimer *timer3 = [NSTimer scheduledTimerWithTimeInterval:2 target:self selector:#selector(showStar4) userInfo:nil repeats:NO];
NSTimer *timer4 = [NSTimer scheduledTimerWithTimeInterval:2 target:self selector:#selector(showStar5) userInfo:nil repeats:NO];
[timer1 fire];
[timer2 fire];
[timer3 fire];
[timer4 fire];
-(void)showStar2
{
[UIView beginAnimations:#"anim1" context:NULL];
[UIView setAnimationDuration:0.5];
[stars[1] setAlpha:1];
[UIView commitAnimations];
}
All the showStar functions are identical except the lines
[UIView beginAnimations:#"anim1" context:NULL];
[stars[1] setAlpha:1];
which have different arguments . stars is an array of UIImageView's.
Any suggestion is welcome .
Actually you are firing all the timers exactly after 2 seconds. Change the timeInterval for different timers.
Instead you can use a single NSTimer that will fire repeatedly.
Declare NSUInteger count; in .h file.
Start a NSTimer as follows:
[NSTimer scheduledTimerWithTimeInterval:2 target:self selector:#selector(showStar:) userInfo:nil repeats:YES];
And your showStar method should be as follows:
-(void)showStar:(NSTimer*)timer
{
if( count > 0 )
{
[stars[count-1] setAlpha:0];
}
[UIView beginAnimations:#"anim1" context:NULL];
[UIView setAnimationDuration:0.5];
[stars[count] setAlpha:1];
[UIView commitAnimations];
count++;
if( count == 4 )
{
[timer invalidate];
count = 0;
}
}
The following code has been added.
if( count > 0 )
{
[stars[count-1] setAlpha:0];
}
All your timers are using the same time delay, so that's what the all appear at once. Also, don't call fire on the timers, they fire automatically. Calling fire probably makes them fire right away.
Incidentally, you don't need to use timers to trigger the animations, you can just add this to your animations:
[UIView setAnimationDelay:x];
And use a different x for each view.
I would do something like this
- (void)firstAnimation{
[UIView animateWithDuration:2.0f animations:^{
//first animation
} completion:^(BOOL finished){
[self secondAnimation];
}];
}
probably with recursion and a flag for current animation would be cleaner though
In my opinion your implementation is wrong.
Better to place a UImageView on your UIView.
Next create an NSArray of images and load then onto the imageView.
arrAnimations = [[NSArray alloc] initWithObjects:[UIImage imageNamed:#"image1.png],.....nil];
then call
imageView.animationImages= arrAnimations;
imageView.animationDuration=2;
[imageView startAnimating];

Resources