iOS animations stop working in my app in iOS7
Asked Answered
I

3

11

I am seeing that all ios animations in my app stop working.Its happening very frequently in iOS7.

I had an app which is supporting iOS 5, 6 and 7. I am seeing recently that all iOS animations stop working in the app in iOS7?

Impressible answered 16/10, 2013 at 19:5 Comment(2)
I don't know the reason behind it but I had exactly same issue and it was resolved when I removed a tableView:willDisplayCell:forRowAtIndexPath: method which used to call lots of performSelectorInBackground to display images. This may help or may give some hint to solve the problem.Borchers
I actually found out.It happens when you do UIKit stuff in the background thread.Here is the link it explains everything. #18281597Impressible
W
24

In IOS 7 when some main method action is performed on background thread then the animations get disabled.

so for this you need to re-enable the animations like (A workaround)

[UIView setAnimationsEnabled:YES];

May be this can help.

Woodnote answered 6/1, 2014 at 11:59 Comment(1)
I am marking it as right although this is a work around.No main method action should be done in background thread.Impressible
D
16

I ran into this problem recently with some views I am laying out for size calculations on the background thread. By swizzling setAnimationsEnabled: I found that the only time I was disabling animations from the background thread was in -[UIImageView setImage:].

Because this view was never rendered and image changes weren't required for my calculation, I was able to enclose this test in a main thread call:

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

It's worth noting I don't hit this issue in the initial view instantiation because I already load my template views in the main thread to avoid a Xib loading issue.

Other issues may be more complex but you should be able to come up with similar workarounds. Here's the category I use to detect background disabling of animations.

#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

Update

It turns out that UIWebView actually makes unsafe calls to setAnimationsEnabled: when displaying a media element (rdar://20314684). This makes the above method very painful to have active all the time if your app allows arbitrary web content. Instead I've started using the below method as it lets me turn the breakpoint on and off and continue after failure:

#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

With this code, you can stop your app by adding a symbolic breakpoint on SEViewAlertForUnsafeBackgroundCalls or just sticking a breakpoint in the function body.

Gist

Demoralize answered 2/10, 2014 at 20:18 Comment(2)
This answer is amazing. Whenever that assertion is thrown, you'll see in your source code where you've messed up and tried to do something not on the main thread.Transposal
I was experiencing this behavior because of a webview that kept making unsafe calls to setAnimationsEnabled: and run into race conditions with other views animating. Is there a way to fix this webview nonsense? Adopting WKWebView is not an option because the webview is encapsulated in a third party class. Would permanently swizzling setAnimationsEnabled: to force it to run in the main be too dangerous?Botts
V
1

Extending Vinay's solution, that's what I do:

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

It seems to solve the problem.

Vadose answered 13/1, 2014 at 14:27 Comment(2)
this is the worst answer that I was expecting :DEsme
what's the outside dispatch async for? Can't you just remove it?Marketable

© 2022 - 2024 — McMap. All rights reserved.