我有一个自定义动画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背景,红色是添加的模态视图控制器,应覆盖整个屏幕。
有人找到了解决方法吗?
答案 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())
})
}
}
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)
现有的答案是部分方式,但不是一直(我们希望在两个设备上都有适当的帧和旋转处理,所有方向,用于动画和交互式转换)。
此博文有助于:
它引用了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)
一种解决方案是进行非常短(或零秒)的转换,然后在转换完成并显示视图控制器后,它将应用正确的转换。然后,您可以在显示的视图控制器中执行动画。