KittenYang

iOS8新特性——UIPresentation在转场动画中的使用

iOS8中苹果给我们来带来了一个新的类—— UIPresentationController。和UIViewControllerTransitioning一样,它也是配合自定义过渡的。在实践中,往往也是配合UIViewControllerTransitioning一起来实现自定义的转场动画。今天我们要实现的一个转场是这样的:

我们的 UIPresentationController 的子类是负责「被呈现」(presented) 及「负责呈现」(presenting) 的 controller 以外的 controller 的,看着很绕口,说白了,在我们的例子中,它负责的仅仅是那个带渐变效果的黑色模糊背景 View和背景视图的动态缩放。

UIPresentationController.h 的内容其实不多,也就80行代码。其中我们我们必须实现的有这四个方法:

- (void)presentationTransitionWillBegin;////在呈现过渡即将开始的时候被调用的

- (void)presentationTransitionDidEnd:(BOOL)completed;////在呈现过渡结束时被调用的

- (void)dismissalTransitionWillBegin;//在退出过渡即将开始的时候被调用的

- (void)dismissalTransitionDidEnd:(BOOL)completed;//在退出的过渡结束时被调用的

按照上面gif显示的效果,我们可以在- (void)presentationTransitionWillBegin;中做如下操作:

- (void)presentationTransitionWillBegin{

    //创建视图
    self.bgView = [[UIView alloc]initWithFrame:[UIScreen mainScreen].bounds];
    self.blurView = [[UIVisualEffectView alloc]initWithEffect:[UIBlurEffect effectWithStyle:UIBlurEffectStyleDark]];
    self.blurView.frame = self.containerView.bounds;
    [self.bgView insertSubview:self.blurView atIndex:0];

    self.contview = self.containerView;
    [self.contview addSubview:self.presentingViewController.view];
    [self.contview addSubview:self.bgView];
    [self.contview addSubview:self.presentedView];



    // 使用 presentingViewController 的 transitionCoordinator,
    // 背景 bgView 的淡入效果与过渡效果一起执行
    self.bgView.alpha = 0.0;
    self.transitionCoordinator = self.presentingViewController.transitionCoordinator;
    [self.transitionCoordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> context) {
        self.bgView.alpha = 0.7;
        self.presentingViewController.view.transform = CGAffineTransformScale(self.presentingViewController.view.transform, 0.92, 0.92);
    } completion:nil];
}

还是比较容易看懂的。无非就是就是新建了一个背景,上面加一个UIVisualEffectView的带模糊效果的视图。然后让背景的alpha从0变到1。同时我们还做了让底下那个视图x、y都缩放到原来的0.92倍。

如果没有完成,移除视图。

//在呈现过渡结束时被调用的
//如果呈现没有完成,那就移除背景 View
- (void)presentationTransitionDidEnd:(BOOL)completed{
    if (!completed) {
        [self.bgView removeFromSuperview];

    }
}

同理,dismissalTransitionWillBegin方法中,我们把背景层的透明度恢复到0.同时也让底下的视图恢复transform.

- (void)dismissalTransitionWillBegin{
    // 与过渡效果一起执行背景 View 的淡出效果
    self.transitionCoordinator = self.presentingViewController.transitionCoordinator;
    [self.transitionCoordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> context) {
        self.bgView.alpha = 0;
        self.presentingViewController.view.transform = CGAffineTransformIdentity;
    } completion:nil];
}

最后,成功完成dismiss之后移除视图。

- (void)dismissalTransitionDidEnd:(BOOL)completed{
    if (completed) {
        [self.bgView removeFromSuperview];
    }

    [[[UIApplication sharedApplication]keyWindow]addSubview:self.presentingViewController.view];
}

这里要说明一点的是,如果没有[[[UIApplication sharedApplication]keyWindow]addSubview:self.presentingViewController.view];这一句的话,dismiss结束之后背景会变黑,原因是presentingViewController随着self.containerView被一同销毁了。我在Stackoverflow终于找到了答案,据说是iOS8的bug,同时解决方案是在window上重新添加一下,也就是[[[UIApplication sharedApplication]keyWindow]addSubview:self.presentingViewController.view];.

使用方式和前三篇博文提到的UIViewControllerTransitioning一样,在presented的ViewController中实现UIViewControllerTransitioningDelegate协议,并且重载下面这个方法就可以了:

- (id <UIViewControllerInteractiveTransitioning>)interactionControllerForDismissal:(id <UIViewControllerAnimatedTransitioning>)animator{

    if (animator) {
        return percentDrivenInteractiveTransition;
    }else{
        return nil;
    }
}

老规矩,全部代码已开源(这里),供学习参考。


转载请注明出处,万分感谢!

KittenYang

写写代码,做做设计,看看产品。