我的iOS应用程序在iOS7中的动画停止工作了。

11

我发现我的应用程序中所有iOS动画都停止工作。在iOS7中经常发生这种情况。

我有一个支持iOS 5、6和7的应用程序。最近我发现在iOS7中,应用程序中的所有iOS动画都停止工作了?


我不知道背后的原因,但我遇到了完全相同的问题,当我删除了一个tableView:willDisplayCell:forRowAtIndexPath:方法时,问题得到了解决,该方法用于调用大量的performSelectorInBackground来显示图像。这可能会有所帮助或提供一些解决问题的提示。 - Mohammad
我实际上发现了,当你在后台线程中进行UIKit操作时会出现这种情况。这里是链接,它解释了一切。https://dev59.com/umMl5IYBdhLWcg3wmn04 - user1010819
3个回答

24

在IOS 7中,如果一些主要的方法操作是在后台线程上执行,则动画会被禁用。

因此,您需要重新启用动画,例如(一种解决方法)

[UIView setAnimationsEnabled:YES];

也许这可以帮助。


3
尽管这只是一个解决办法,但我会将其标记为正确的。不应该在后台线程中执行任何主要的方法操作。 - user1010819

16

最近在为背景线程上的视图大小计算布局时遇到了这个问题。通过交换 setAnimationsEnabled:,我发现唯一禁用背景线程中的动画的时间是在 -[UIImageView setImage:] 中。

因为这个视图从未被渲染,而且图片更改对我的计算也不需要,所以我能够将这个测试包含在一个主线程调用中:

if ([NSThread isMainThread]) {
    self.answerImageView.image = [UIImage imageNamed:imgName];
}

值得注意的是,在初始视图实例化中,我不会遇到这个问题,因为我已经在主线程中加载我的模板视图,以避免Xib加载问题。

其他问题可能更加复杂,但你应该能够想出类似的解决方法。这是我用来检测动画背景禁用的类别。

#import <UIKit/UIKit.h>
#import <JRSwizzle/JRSwizzle.h>

#ifdef DEBUG

@implementation UIView (BadBackgroundBehavior)

+ (void)load
{
    NSError *error = nil;
    if (![self jr_swizzleClassMethod:@selector(setAnimationsEnabled:) withClassMethod:@selector(SE_setAnimationsEnabled:) error:&error]) {
        NSLog(@"Error! %@", error);
    }
}

+ (void)SE_setAnimationsEnabled:(BOOL)enabled
{
    NSAssert([NSThread isMainThread], @"This method is not thread safe. Look at the backtrace and decide if you really need to be doing this here.");
    [self SE_setAnimationsEnabled:enabled];
}

@end

#endif

更新

事实证明,UIWebView在显示媒体元素时实际上会进行不安全的setAnimationsEnabled:调用(rdar://20314684)。 如果您的应用程序允许任意网络内容,则使上述方法始终处于活动状态非常痛苦。 因此,我已经开始使用下面的方法,因为它可以让我在失败后打开和关闭断点并继续执行:

#import <UIKit/UIKit.h>
#import <objc/runtime.h>

#ifdef DEBUG

void SEViewAlertForUnsafeBackgroundCalls() {
    NSLog(@"----------------------------------------------------------------------------------");
    NSLog(@"Background call to setAnimationsEnabled: detected. This method is not thread safe.");
    NSLog(@"Set a breakpoint at SEUIViewDidSetAnimationsOffMainThread to inspect this call.");
    NSLog(@"----------------------------------------------------------------------------------");
}

@implementation UIView (BadBackgroundBehavior)

+ (void)load
{
    method_exchangeImplementations(class_getInstanceMethod(object_getClass(self), @selector(setAnimationsEnabled:)),
                                   class_getInstanceMethod(object_getClass(self), @selector(SE_setAnimationsEnabled:)));
}

+ (void)SE_setAnimationsEnabled:(BOOL)enabled
{
    if (![NSThread isMainThread]) {
        SEViewAlertForUnsafeBackgroundCalls();
    }
    [self SE_setAnimationsEnabled:enabled];
}

@end

#endif

使用此代码,您可以在SEViewAlertForUnsafeBackgroundCalls上添加一个符号断点或仅将断点插入函数体中来停止应用程序。

Gist


这个答案太棒了。每当抛出那个断言时,你会在源代码中看到自己搞砸了并尝试在主线程之外做一些事情。 - Ian MacDonald
我遇到了这个问题,因为一个Webview一直在进行不安全的setAnimationsEnabled:调用,并与其他视图动画产生竞争条件。有没有办法修复这个Webview的问题?采用WKWebView不是一个选项,因为Webview封装在第三方类中。将setAnimationsEnabled:永久交换以强制在主线程中运行是否太危险? - ArkReversed

1
扩展Vinay的解决方案,这就是我所做的:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            //make calculations
            dispatch_async(dispatch_get_main_queue(),
                           ^{
                                [UIView setAnimationsEnabled:YES];
                           });           
});

它似乎解决了问题。


这是我最不期望的答案 :D - l0gg3r
外部调度异步是干什么用的?你不能直接移除它吗? - AntiMoron

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接