横向自定义UIViewController动画的解决方法?

时间:2013-11-16 02:32:04

标签: uiviewcontroller ios7 modalviewcontroller

我有一个自定义动画UIViewController转换,似乎iOS中存在一个错误,它以横向方向搞砸了布局。在主要的动画方法中,我给出了混合的横向和纵向视图。 (在肖像中,视图都是肖像,所以没问题。)

- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext;
{
  UIViewController* toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
  UIViewController* fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
  UIView *containerView = [transitionContext containerView];

  // fromViewController.view => landscape, transform
  // toViewController.view => portrait, transform
  // containerView => portrait, no transform

  [containerView addSubview:toViewController.view];

  // ...animation... //
}

我知道当视图有变换时frame属性不可靠 - 所以我猜这是问题的根源。在横向模式下,to / from viewControllers视图具有90度顺时针变换[0 -1 1 0]。我已经尝试使用边界/中心来调整视图的大小和位置,以及删除变换然后重新应用它,但UIKit与我战斗并坚持将视图显示为肖像。讨厌!

在屏幕截图中,深灰色是UIWindow背景,红色是添加的模态视图控制器,应覆盖整个屏幕。

the red view is in portrait

有人找到了解决方法吗?

4 个答案:

答案 0 :(得分:17)

好的,修复非常简单:

将toViewController框架设置为容器,然后将视图添加到容器中。

toViewController.view.frame = containerView.frame;
[containerView addSubview:toViewController.view];

更新:您仍然存在一个限制,即您不知道框架的方向。它最初是肖像,但在屏幕上显示时会延伸到横向。如果你想在右边的视图中滑动,在横向上它可能会从“顶部”滑入(如果查看其他景观,则从底部滑入!)

答案 1 :(得分:13)

我遇到过这个问题而我并不认为上述解决方案可以做到这一点。我提出了一个不需要hacky代码和硬编码帧的解决方案。

UIView有一个很棒的功能,可以将CGRect转换为另一个的坐标空间(即+[UIView convertRect:fromView:])。因此,我希望详细介绍一种可以在任何方向上实现此效果的简单方法,而无需任何硬编码值。在这个例子中,我们想要一个简单的动画,从屏幕右侧滑动视图。

因此,在我们的动画师animateTransition(:)中,我们可以简单地执行以下操作:

夫特

func animateTransition(transitionContext: UIViewControllerContextTransitioning) {
    let toViewController = transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey)!
    let fromViewController = transitionContext.viewControllerForKey(UITransitionContextFromViewControllerKey)!

    let toView = toViewController.view
    let fromView = fromViewController.view
    let containerView = transitionContext.containerView()

    if(isPresenting) {
        //now we want to slide in from the right
        let startingRect = CGRectOffset(fromView.bounds, CGRectGetWidth(fromView.bounds), 0)
        toView.frame = containerView.convertRect(startingRect, fromView:fromView);
        containerView.addSubview(toView)
        let destinationRect = containerView.convertRect(fromView.bounds, fromView: fromView)
        UIView.animateWithDuration(transitionDuration(transitionContext),
            delay: 0,
            usingSpringWithDamping: 0.7,
            initialSpringVelocity: 0.7,
            options: .BeginFromCurrentState,
            animations: { () -> Void in
                toView.frame = destinationRect
        }, completion: { (complete) -> Void in
            transitionContext.completeTransition(!transitionContext.transitionWasCancelled())
        })
    } else {
        //we want to slide out to the right
        let endingRect = containerView.convertRect(CGRectOffset(fromView.bounds, CGRectGetWidth(fromView.bounds), 0), fromView: fromView)
        UIView.animateWithDuration(transitionDuration(transitionContext),
            delay: 0,
            usingSpringWithDamping: 0.7,
            initialSpringVelocity: 0.7,
            options: .BeginFromCurrentState,
            animations: { () -> Void in
                fromView.frame = endingRect
            }, completion: { (complete) -> Void in
                if !transitionContext.transitionWasCancelled() {
                    fromView.removeFromSuperview()
                }
                transitionContext.completeTransition(!transitionContext.transitionWasCancelled())
        })
    }
}

目标C

UIViewController* toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
UIViewController* fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];

UIView *toView = toViewController.view;
UIView *fromView = fromViewController.view;
UIView *containerView = [transitionContext containerView];

if(self.isPresenting) {
    //now we want to slide in from the right
    CGRect startingRect = CGRectOffset(fromView.bounds, CGRectGetWidth(fromView.bounds), 0);
    toView.frame = [containerView convertRect:startingRect fromView:fromView];
    [containerView addSubview:toView];
    [UIView animateWithDuration:[self transitionDuration:transitionContext]
                     animations:^{
                         toView.frame = [containerView convertRect:fromView.bounds
                                                          fromView:fromView];
                     }
                     completion:^(BOOL finished) {
                         [transitionContext completeTransition:![transitionContext transitionWasCancelled]];
                     }];

} else {
    //we want to slide out to the right
    [UIView animateWithDuration:[self transitionDuration:transitionContext]
                     animations:^{
                         CGRect endingRect = CGRectOffset(fromView.bounds, CGRectGetWidth(fromView.bounds), 0);
                         fromView.frame = [containerView convertRect:endingRect fromView:fromView];
                     }
                     completion:^(BOOL finished) {
                         [fromView removeFromSuperview];
                         [transitionContext completeTransition:![transitionContext transitionWasCancelled]];
                     }];

}

我希望这可以帮助那些在同一条船上来到这里的人(如果有的话,投票不会受到影响:))

答案 2 :(得分:8)

现有的答案是部分方式,但不是一直(我们希望在两个设备上都有适当的帧和旋转处理,所有方向,用于动画和交互式转换)。

此博文有助于:

http://www.brightec.co.uk/blog/ios-7-custom-view-controller-transitions-and-rotation-making-it-all-work

它引用了Apple支持人员陈述问题的真实性质:

  

&#34;对于自定义演示文稿转换,我们在窗口和windows rootViewController视图之间设置了一个中间视图。此视图是您在其中执行动画的containerView。由于iOS上自动旋转的实现细节,当界面旋转时,我们将仿射变换应用于windows rootViewController的视图并相应地修改其边界。因为containerView从窗口而不是根视图控制器的视图继承其尺寸,所以它始终处于纵向方向。&#34;

     

&#34;如果您的演示动画取决于呈现视图控制器的方向,则需要检测呈现视图控制器的方向并适当地修改动画。系统会将正确的变换应用于传入的视图控制器,但您的动画制作者需要配置传入视图控制器的帧。&#34;

但它没有解决交互式过渡问题。

我在这里找到了解决问题的完整解决方案:

https://github.com/alfiehanssen/Cards

基本上,您需要根据viewControllers之一(toViewController或fromViewController)的方向计算viewControllers的帧,而不是transitionContext的containerView的边界。

答案 3 :(得分:-1)

一种解决方案是进行非常短(或零秒)的转换,然后在转换完成并显示视图控制器后,它将应用正确的转换。然后,您可以在显示的视图控制器中执行动画。