31

I am presenting a UIViewController that contains a UIVisualEffectView as follows:

-(void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
    [self performSegueWithIdentifier:@"segueBlur" sender:nil];
}

-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
    if([segue.identifier isEqualToString:@"segueBlur"]) {
        ((UIViewController *)segue.destinationViewController).providesPresentationContextTransitionStyle = YES;
        ((UIViewController *)segue.destinationViewController).definesPresentationContext = YES;
        ((UIViewController *)segue.destinationViewController).modalPresentationStyle = UIModalPresentationOverFullScreen;
    }
}

As you can see, I'm using the UIModalPresentationStyleOverFullScreen so that when the view controller with blur appears, the blur will be 'applied' to the content of the view controller that is presenting it; the segue has a Cross Dissolve transition style.

The effect looks as expected. However, in iOS 9 the presentation is smoother than in iOS 10. In iOS 10 when the view controller appears it seems like a 2-step animation, while in iOS 9 the blur is applied immediately.

A picture is worth a thousand words so I uploaded a video showing this strange behavior:

UIVisualEffectView iOS 9 vs iOS 10

My question is: How can I present the view controller in iOS 10 as it is presented in iOS 9?

JAL
  • 41,701
  • 23
  • 172
  • 300
Axort
  • 2,034
  • 2
  • 23
  • 24

3 Answers3

27

iOS 10 has changed the way UIVisualEffectView works, and it has broken many use cases which were not strictly speaking "legal", but worked before. Sticking to documentation, you should not be fading in UIVisualEffectView, which is what happens when you use UIModalTransitionStyleCrossDissolve. It seems now broken on iOS 10, along with masking of visual effect views, and others.

In your case, I would suggest an easy fix which will also create a better effect than before, and is supported on both iOS 9 and 10. Create a custom presentation and instead of fading the view in, animate the effect property from nil to the blur effect. You can fade in the rest of your view hierarchy if needed. This will neatly animate the blur radius similar to how it looks when you pull the home screen icons down.

Léo Natan
  • 56,823
  • 9
  • 150
  • 195
  • 3
    Awesome! Works great. I used the same project I used for the video I posted and uploaded it to GitHub with your solution. Thank you. > https://github.com/Axort/BlurTest-iOS10 – Axort Sep 27 '16 at 17:21
  • 1
    I suggest using a spring animation, and a duration of 0.5. This is what the native does. – Léo Natan Sep 27 '16 at 17:33
  • @Axort do you have a swift version of that? – user2722667 Oct 20 '16 at 16:27
  • @user2722667 Currently no ): – Axort Oct 20 '16 at 16:31
  • 3
    Thanks for this. It works as advertised Just note that you have to first set your visual effect view's effect property to nil. Then you animate TO your target "effect". (e.g. light). And you can't set the alpha of either the visual effect view, or any superview. (That works, sort-of, in iOS 9, and not at all in iOS 10.) Another fine API brought to you by Apple. All I can say is thanks for Stack Overflow and its contributors... – Womble Nov 17 '16 at 06:29
  • 1
    @Axort thanks for your github solution, works perfectly!! – sudoExclaimationExclaimation Nov 27 '16 at 20:02
5
    UIView.animate(withDuration: 0.5) {
        self.effectView.effect = UIBlurEffect(style: .light)
    }

Tested on both iOS9 and 10. Works fine for me

Daniel Storm
  • 18,301
  • 9
  • 84
  • 152
Allen Zhao
  • 53
  • 2
3

Code below blur parent view controller when ViewController presented. Tested on both iOS9 and 10.

@interface ViewController () <UIViewControllerTransitioningDelegate, UIViewControllerAnimatedTransitioning>

@property (nonatomic) UIVisualEffectView *blurView;

@end


@implementation ViewController

- (instancetype)init
{
    self = [super init];
    if (!self)
        return nil;

    self.modalPresentationStyle = UIModalPresentationOverCurrentContext;
    self.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
    self.transitioningDelegate = self;

    return self;
}

- (void)viewDidLoad
{
    [super viewDidLoad];

    self.view.backgroundColor = [UIColor clearColor];
    self.blurView = [UIVisualEffectView new];
    [self.view addSubview:self.blurView];
    self.blurView.frame = self.view.bounds;
}

- (id <UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented
                                                                       presentingController:(UIViewController *)presenting
                                                                   sourceController:(UIViewController *)source
{
    return self;
}

-(NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext
{
    return 0.3;
}

-(void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext
{
    UIView *container = [transitionContext containerView];

    [container addSubview:self.view];

    self.blurView.effect = nil;

    [UIView animateWithDuration:0.3 animations:^{
        self.blurView.effect = [UIBlurEffect effectWithStyle:UIBlurEffectStyleDark];
    } completion:^(BOOL finished) {
        [transitionContext completeTransition:finished];
    }];
}

@end