最近根据产品需求, 更改翻转动画, 很多时候我们只是改一下 to view 的 frame 来满足需求, 但当要 from view 动起来就出现很多坑了.

首先说一下通过 viewControllerForKey然后.view来获取 to view 和 from view 与使用viewForKey来获取的区别, 如果 to 和 from view 都与 animator 关联, 那么就没有区别了, 但苹果在这里设置了一个坑, 解释如下:

(本文说的 from view 是 present 时候的 from view, dismiss 时候的 to view)

1
2
3
4
5
// Currently only two keys are defined by the system -
// UITransitionContextFromViewKey, and UITransitionContextToViewKey
// viewForKey: may return nil which would indicate that the animator should not
// manipulate the associated view controller's view.
- (nullable __kindof UIView *)viewForKey:(UITransitionContextViewKey)key NS_AVAILABLE_IOS(8_0);

ok, 如果使用 viewForKey 获取不到 view, 那么我们使用 viewControllerForKey 获取 view 不就肯定没问题了吗?
答案是错误的, 因为 如果 to 和 from view 没和 animator 关联, 那么这个 view 就不可以加交互动画, 当你 dismiss 的时候动画立刻执行完成, 根本不受 updateInteractiveTransition 的控制, 说到这里我们必须说一下什么情况会发生未关联这种情况, 看苹果解释:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// If NO is returned from -shouldRemovePresentersView, the view associated
// with UITransitionContextFromViewKey is nil during presentation. This
// intended to be a hint that your animator should NOT be manipulating the
// presenting view controller's view. For a dismissal, the -presentedView
// is returned.
//
// Why not allow the animator manipulate the presenting view controller's
// view at all times? First of all, if the presenting view controller's
// view is going to stay visible after the animation finishes during the
// whole presentation life cycle there is no need to animate it at all — it
// just stays where it is. Second, if the ownership for that view
// controller is transferred to the presentation controller, the
// presentation controller will most likely not know how to layout that
// view controller's view when needed, for example when the orientation
// changes, but the original owner of the presenting view controller does.
UIView *fromView = [transitionContext viewForKey:UITransitionContextFromViewKey];
// Indicate whether the view controller's view we are transitioning from will be removed from the window in the end of the
// presentation transition
// (Default: NO)
@property(nonatomic, readonly) BOOL shouldRemovePresentersView;

解释具体位置

shouldRemovePresentersView这个属性在哪里那?
苹果提供 管理 交互动画的 控制器 UIPresentationController,
只要继承这个类, 并重写你需要的方法, 就可以在交互过程中, 完成你想要的效果, 重写下面方法:

1
2
3
4
- (void)presentationTransitionWillBegin;
- (void)presentationTransitionDidEnd:(BOOL)completed;
- (void)dismissalTransitionWillBegin;
- (void)dismissalTransitionDidEnd:(BOOL)completed;

说了这么多, 回到主题, 如何让 from view 和 animator 关联,
只要继承 UIPresentationController类, 自定义一个SomePresentationController重写 shouldRemovePresentersView get方法, 返回 YES, 就关联了, default 是 NO, 如果不重写, 你永远获取不到 from view.

同时, 需要实现 UIViewControllerTransitioningDelegate 的相应代理方法, 如下:

1
2
3
4
- (UIPresentationController *)presentationControllerForPresentedViewController:(UIViewController *)presented presentingViewController:(UIViewController *)presenting sourceViewController:(UIViewController *)source {
SomePresentationController *presentation = [[SomePresentationController alloc]initWithPresentedViewController:presented presentingViewController:presenting];
return presentation;
}
1
2
3
- (BOOL)shouldRemovePresentersView {
return YES;
}

最后说几句, 如果不看苹果的那个 demo , 你可能永远不知道自己错哪了, 当我看到下面的那几句解释, 整个人都释然了.

1
2
3
4
5
// If NO is returned from -shouldRemovePresentersView, the view associated
// with UITransitionContextFromViewKey is nil during presentation. This
// intended to be a hint that your animator should NOT be manipulating the
// presenting view controller's view. For a dismissal, the -presentedView
// is returned.

苹果事例链接

苹果的 demo