确切的时刻,iOS在进入背景时会获取视图快照? [英] The exact moment iOS takes the view snapshot when entering background?

查看:561
本文介绍了确切的时刻,iOS在进入背景时会获取视图快照?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

通过按下退出按钮将iPhone应用程序放置到背景上时,我有一个问题,然后通过点击主屏幕上的启动图标来重新启动:应用程序的视图将恢复到初始状态,就像我想要的那样,但之前它会在屏幕上短暂地闪烁较早的错误视图状态。



背景



我的主视图基本上包含一系列互连的UIAnimateWithDuration调用。每当发生任何中断时,我想要的行为是将动画重置为初始状态(除非动画已经完成,并且应用程序已进入静态最后阶段),并且每当应用程序返回到活动和可见状态时从那里重新开始



在学习主题之后,我学到了我需要两种类型的中断处理代码,以提供良好的ux:即时和平滑。我有一个方法resetAnimation,立即将视图属性重置为初始状态,并将方法pauseAnimation快速动画到相同的状态,另外一个附加标签表示视图顶部的已暂停渐变。



双击退出按钮



原因是双击退出按钮用例,实际上并不隐藏您的视图或将其置于背景状态,它只是向上滚动一下以显示底部的多任务菜单。所以在这种情况下立即重置视图状态看起来很丑陋。动画过渡并告诉您暂停的用户似乎是一个更好的主意。



通过在我的App Delegate中实现applicationWillResignActive委托方法并调用pauseAnimation从那里。我通过实现applicationDidBecomeActive委托方法处理从多任务菜单返回,并从那里调用我的resumeAnimation方法,如果它在那里淡出已暂停标签,并从初始状态启动我的动画序列。



这一切都正常,没有任何闪烁。



访问翻转



我的应用程序建立在Xcode实用程序模板上,因此它具有显示信息/设置的反向视图。我在主视图控制器中通过实现这两个代理方法来处理访问主视图并返回主视图:




  • 无效)viewDidDisappear:(BOOL)animated


  • (void)viewDidAppear:(BOOL)animated




我在viewDidDisappear方法中调用我的resetAnimation,并在viewDidAppear中恢复A动画。这一切都很好,主视图是从转换到可见状态的最初状态 - 没有意外闪烁任何错误的动画状态。但是:



推出退出按钮并从我的应用图标重新启动(越野车部分!)



这是麻烦开始的地方。当我按下退出按钮一次,我的应用程序开始转换到背景,两件事情发生。首先,applicationWillResignActive在这里也被调用,所以我的pauseAnimation方法也会启动。不需要,因为转换不需要在这里平稳 - 视图静态,并且缩小显示主屏幕 - 但是你能做什么?那么,如果我只是可以在系统拍摄视图的快照之前调用resetAnimation,那就不会有任何伤害。



无论如何,其次,applicationDidEnterBackground在App代理中被调用。我试图从那里调用resetAnimation,以便在应用程序返回时视图将处于正确的状态,但这似乎不起作用。似乎已经采取了快照,当我点击我的应用程序启动图标并重新启动时,在正确的初始状态显示之前,错误的视图状态会在屏幕上短暂闪烁。之后,它的工作正常,动画就像他们应该这样做,但那个重新启动的时刻,那个丑陋的闪烁不会消失,无论我在尝试什么。



从根本上说,我以后是什么,系统采取这个快照的确切时刻?因此,正确的代理方法或通知处理程序将准备我的纪念照片视图是什么?



PS。然后有default.png,它似乎不会在第一次启动时显示,而且每当处理器有困难或返回到应用程序时,由于某些其他原因而被延迟。这有点丑陋,特别是如果你回到与您的默认视图完全不同的翻转视图。但是这是一个核心的iOS功能,我猜想我甚至不应该弄清楚或控制那个:)






编辑:由于人们正在要求实际的代码,我的应用程序在提出这个问题后已经被释放,我会在这里发布一些。 (这个应用程序叫Sweetest Kid,如果你想看看它是如何工作的,那就在这里: http://itunes.apple.com/app/sweetest-kid/id476637106?mt=8



这是我的pauseAnimation方法 - resetAnimation几乎相同,除了它的动画调用具有零持续时间和延迟,并且不显示暂停标签。我使用UIAnimation重置值而不是分配新值的一个原因是,由于某些原因,如果我没有使用UIAnimation,动画就没有停止。无论如何,这里是pauseAnimation方法:

   - (void)pauseAnimation {
if(currentAnimationPhase< 6 || currentAnimationPhase == 255){
// 6表示完成,255是一个简短的初始动画,仅在第一次启动时显示
self.paused = YES;
[UIView animateWithDuration:0.3
delay:0
选项:UIViewAnimationOptionAllowUserInteraction |
UIViewAnimationOptionBeginFromCurrentState |
UIViewAnimationOptionCurveEaseInOut |
UIViewAnimationOptionOverrideInheritedCurve |
UIViewAnimationOptionOverrideInheritedDuration
动画:^ {
pausedView.alpha = 1.0;
cameraImageView.alpha = 0;
mirrorGlowView.alpha = 0;
infoButton.alpha = 1.0;
chantView.alpha = 0;
verseOneLabel.alpha = 1.0;
verseTwoLabel.alpha = 0;
verseThreeLabel.alpha = 0;
shine1View.alpha = stars1View.alpha = stars2View.alpha = 0;
shine1View.transform = CGAffineTransformIdentity;
stars1View.transform = CGAffineTransformIdentity;
stars2View.transform = CGAffineTransformIdentity;
finishedMenuView.alpha = 0;
prepareMagicView.alpha = 0;}
完成:^(BOOL完成){
pausedView.alpha = 1.0;
cameraImageView.alpha = 0;
mirrorGlowView.alpha = 0;
infoButton.alpha = 1.0;
chantView.alpha = 0;
verseOneLabel.alpha = 1.0;
verseTwoLabel.alpha = 0;
verseThreeLabel.alpha = 0;
shine1View.alpha = stars1View.alpha = stars2View.alpha = 0;
shine1View.transform = CGAffineTransformIdentity;
stars1View.transform = CGAffineTransformIdentity;
stars2View.transform = CGAffineTransformIdentity;
finishedMenuView.alpha = 0;
prepareMagicView.alpha = 0;
}];
askTheMirrorButton.enabled = YES;
againButton.enabled = NO;
shareOnFacebookButton.enabled = NO;
emailButton.enabled = NO;
saveButton.enabled = NO;
currentAnimationPhase = 0;
[[cameraImageView subviews] makeObjectsPerformSelector:@selector(removeFromSuperview)]; //删除视频预览图层
}
}


解决方案

该方法返回后立即采取截图。我猜你的-resetAnimation方法在下一个循环循环中完成,而不是立即执行。
我还没有尝试过,但是您可以尝试让跑马跑运行,然后稍后返回:

   - (void)applicationDidEnterBackground:(UIApplication *)application {
//你的代码在这里

//让runloop运行一个短暂的时间
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.01]];
}

我希望这有帮助,
Fabian






更新:-pauseAnimation和-resetAnimation区别



方法:延迟在-applicationWillResignActive中发生的动画:并取消-applicationDidEnterBackground中的延迟动画:

   - (void)applicationWillResignActive :( UIApplication *)应用程序{
//首先测量-applicationWillResignActive:和-applicationDidEnterBackground之间的时间!
[self performSelector:@selector(pauseAnimation)withObject:nil afterDelay:0.1];

//其他代码
}

- (void)applicationDidEnterBackground:(UIApplication *)application {
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@ selector(pauseAnimation)对象:nil];

//其他代码在这里
}


I have a problem when putting my iPhone app to background by pushing the exit button, and then relaunching by tapping the launch icon on the home screen: the app's view does return to its initial state like I want it to, but before that it flashes the earlier, wrong view state onscreen briefly.

Background

My main view consists basically of a sequence of interlinked UIAnimateWithDuration calls. The behavior I want whenever any interruption occurs, is to reset the animation to its initial state (unless the animations have all finished and the app has entered the static final phase), and start over from there whenever the app returns to active and visible state.

After studying the subject I learned I need two types of interruption handling code to provide good ux: "instant" and "smooth". I have the method resetAnimation that resets the view properties to the initial state instantly, and the method pauseAnimation that animates quickly to the same state, with an additional label stating "paused" fading in on the top of the view.

Double clicking exit button

The reason for this is the "double clicking exit button" use case, that actually does not hide your view or put you in the background state, it just scrolls up a bit to show the multitasking menu at the bottom. So, resetting the view state instantly in this case just looked very ugly. The animated transition and telling the user you're paused seemed like a better idea.

This case works nice and smootly by implementing the applicationWillResignActive delegate method in my App Delegate and calling pauseAnimation from there. I handle returning from that multitasking menu by implementing the applicationDidBecomeActive delegate method and calling from there my resumeAnimation method, that fades out the "paused" label if its there, and starts my animation sequence from the initial state.

This all works fine, no flickering anywhere.

Visiting flipside

My app's built over the Xcode "utility" template, so it has a flipside view to show info/settings. I handle visiting the flipside and returning back to the main view by implementing these two delegate methods in my main view controller:

  • (void)viewDidDisappear:(BOOL)animated

  • (void)viewDidAppear:(BOOL)animated

I call my resetAnimation in the viewDidDisappear method and resumeAnimation in viewDidAppear. This all works fine, the main view is its initial state from the very beginning of the transition to visible state - no unexpected flashing of wrong animation states of anything. But:

Pushing exit button and relaunching from my app icon (the buggy part!)

This is where the trouble starts. When I push exit button once and my app begins its transition to background, two things happen. First, applicationWillResignActive gets called here too, so my pauseAnimation method launches also. It wouldn't need to, since the transition doesn't need to be smooth here – the view just goes static, and "zooms out" to reveal the home screen – but what can you do? Well, it wouldn't do any harm either if I just could call resetAnimation before the exact moment that the system takes the snapshot of the view.

Anyways, secondly, applicationDidEnterBackground in the App Delegate gets called. I tried to call resetAnimation from there so that the view would be in the right state when the app returns, but this doesn't seem to work. It seems the "snapshot" has been taken already and so, when I tap my app launch icon and relauch, the wrong view state does flash briefly on the screen before the correct, initial state shows. After that, it works fine, the animations go about like they're supposed to, but that ugly flicker at that relaunch moment won't go away, no matter what I try.

Fundamentally, what I'm after is, what exact moment does the system take this snapshot? And consequently, what would be the correct delegate method or notification handler to prepare my view for taking the "souvenir photo"?

PS. Then there's the default.png, which doesn't seem to only show at first launch, but also whenever the processor's having a hard time or returning to the app is delayed briefly for some other reason. It's a bit ugly, especially if you're returning to your flipside view that looks totally different from your default view. But this is such a core iOS feature, I'm guessing I shouldn't even try to figure out or control that one :)


Edit: since people were asking for actual code, and my app has already been released after asking this question, I'll post some here. ( The app's called Sweetest Kid, and if you want to see how it actually works, it's here: http://itunes.apple.com/app/sweetest-kid/id476637106?mt=8 )

Here's my pauseAnimation method – resetAnimation is almost identical, except its animation call has zero duration and delay, and it doesn't show the 'Paused' label. One reason I'm using UIAnimation to reset the values instead of just assigning the new values is that for some reason, the animations just didn't stop if I didn't use UIAnimation. Anyway, here's the pauseAnimation method:

    - (void)pauseAnimation {
    if (currentAnimationPhase < 6 || currentAnimationPhase == 255) { 
            // 6 means finished, 255 is a short initial animation only showing at first launch
        self.paused = YES;
        [UIView animateWithDuration:0.3
                              delay:0 
                            options:UIViewAnimationOptionAllowUserInteraction |
         UIViewAnimationOptionBeginFromCurrentState |
         UIViewAnimationOptionCurveEaseInOut |
         UIViewAnimationOptionOverrideInheritedCurve |
         UIViewAnimationOptionOverrideInheritedDuration
                         animations:^{
                             pausedView.alpha = 1.0;
                             cameraImageView.alpha = 0;
                             mirrorGlowView.alpha = 0;
                             infoButton.alpha = 1.0;
                             chantView.alpha = 0; 
                             verseOneLabel.alpha = 1.0;
                             verseTwoLabel.alpha = 0; 
                             verseThreeLabel.alpha = 0;
                             shine1View.alpha = stars1View.alpha = stars2View.alpha = 0;
                             shine1View.transform = CGAffineTransformIdentity;
                             stars1View.transform = CGAffineTransformIdentity;
                             stars2View.transform = CGAffineTransformIdentity;
                             finishedMenuView.alpha = 0;
                             preparingMagicView.alpha = 0;}
                         completion:^(BOOL finished){
                             pausedView.alpha = 1.0;
                             cameraImageView.alpha = 0;
                             mirrorGlowView.alpha = 0;
                             infoButton.alpha = 1.0;
                             chantView.alpha = 0; 
                             verseOneLabel.alpha = 1.0;
                             verseTwoLabel.alpha = 0; 
                             verseThreeLabel.alpha = 0;
                             shine1View.alpha = stars1View.alpha = stars2View.alpha = 0;
                             shine1View.transform = CGAffineTransformIdentity;
                             stars1View.transform = CGAffineTransformIdentity;
                             stars2View.transform = CGAffineTransformIdentity;
                             finishedMenuView.alpha = 0;
                             preparingMagicView.alpha = 0;
                         }];
        askTheMirrorButton.enabled = YES; 
        againButton.enabled = NO;
        shareOnFacebookButton.enabled = NO;
        emailButton.enabled = NO;
        saveButton.enabled = NO;
        currentAnimationPhase = 0;
        [[cameraImageView subviews] makeObjectsPerformSelector:@selector(removeFromSuperview)]; // To remove the video preview layer
    }
}

解决方案

The screenshot is taken immediately after this method returns. I guess your -resetAnimation method completes in the next runloop cycle and not immediately. I've not tried this, but you could try to let the runloop run and then return a little bit later:

- (void) applicationDidEnterBackground:(UIApplication *)application {
    // YOUR CODE HERE

    // Let the runloop run for a brief moment
    [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.01]];
}

I hope this helps, Fabian


Update: -pauseAnimation and -resetAnimation distinction

Approach: Delay the animation happening in -applicationWillResignActive: and cancel the delayed animation in -applicationDidEnterBackground:

- (void) applicationWillResignActive:(UIApplication *)application {
    // Measure the time between -applicationWillResignActive: and -applicationDidEnterBackground first!
    [self performSelector:@selector(pauseAnimation) withObject:nil afterDelay:0.1];

    // OTHER CODE HERE
}

- (void) applicationDidEnterBackground:(UIApplication *)application {
    [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(pauseAnimation) object:nil];

    // OTHER CODE HERE
}

这篇关于确切的时刻,iOS在进入背景时会获取视图快照?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆