我已经实现了一个导航控制器,以结合旋转盘类型的布局(想象每个VC布局成一个圆圈,整体旋转,顺序进入视图 . 导航控制器配置为使用自定义过渡类,如下所示: -
import UIKit
class RotaryTransition: NSObject, UIViewControllerAnimatedTransitioning {
let isPresenting :Bool
let duration :TimeInterval = 0.5
let animationDuration: TimeInterval = 0.7
let delay: TimeInterval = 0
let damping: CGFloat = 1.4
let spring: CGFloat = 6.0
init(isPresenting: Bool) {
self.isPresenting = isPresenting
super.init()
}
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
//Get references to the view hierarchy
let fromViewController: UIViewController = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from)!
let toViewController: UIViewController = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to)!
let sourceRect: CGRect = transitionContext.initialFrame(for: fromViewController)
let containerView: UIView = transitionContext.containerView
if self.isPresenting { // Push
//1. Settings for the fromVC ............................
// fromViewController.view.frame = sourceRect
fromViewController.view.layer.anchorPoint = CGPoint(x: 0.5, y: 3);
fromViewController.view.layer.position = CGPoint(x: fromViewController.view.frame.size.width/2, y: fromViewController.view.frame.size.height * 3);
//2. Setup toVC view...........................
containerView.insertSubview(toViewController.view, belowSubview:fromViewController.view)
toViewController.view.layer.anchorPoint = CGPoint(x: 0.5, y: 3);
toViewController.view.layer.position = CGPoint(x: toViewController.view.frame.size.width/2, y: toViewController.view.frame.size.height * 3);
toViewController.view.transform = CGAffineTransform(rotationAngle: 15 * CGFloat(M_PI / 180));
//3. Perform the animation...............................
UIView.animate(withDuration: animationDuration, delay:delay, usingSpringWithDamping: damping, initialSpringVelocity: spring, options: [], animations: {
fromViewController.view.transform = CGAffineTransform(rotationAngle: -15 * CGFloat(M_PI / 180));
toViewController.view.transform = CGAffineTransform(rotationAngle: 0);
}, completion: {
(animated: Bool) -> () in transitionContext.completeTransition(true)
})
} else { // Pop
//1. Settings for the fromVC ............................
fromViewController.view.frame = sourceRect
fromViewController.view.layer.anchorPoint = CGPoint(x: 0.5, y: 3);
fromViewController.view.layer.position = CGPoint(x: fromViewController.view.frame.size.width/2, y: fromViewController.view.frame.size.height * 3);
//2. Setup toVC view...........................
// toViewController.view.frame = transitionContext.finalFrame(for: toViewController)
toViewController.view.layer.anchorPoint = CGPoint(x: 0.5, y: 3);
toViewController.view.layer.position = CGPoint(x: toViewController.view.frame.size.width/2, y: toViewController.view.frame.size.height * 3);
toViewController.view.transform = CGAffineTransform(rotationAngle: -15 * CGFloat(M_PI / 180));
containerView.insertSubview(toViewController.view, belowSubview:fromViewController.view)
//3. Perform the animation...............................
UIView.animate(withDuration: animationDuration, delay:delay, usingSpringWithDamping: damping, initialSpringVelocity: spring, options: [], animations: {
fromViewController.view.transform = CGAffineTransform(rotationAngle: 15 * CGFloat(M_PI / 180));
toViewController.view.transform = CGAffineTransform(rotationAngle: 0);
}, completion: {
//When the animation is completed call completeTransition
(animated: Bool) -> () in transitionContext.completeTransition(true)
})
}
}
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return duration;
}
}
下图中显示了视图如何移动的表示......两个红色区域是问题,如后面所述 .
呈现(推)翻译工作正常 - 2移动到1和3移动到2.然而,解雇(弹出)翻译没有,从而解雇VC看似正确地移出视图(2移动到3),但呈现(上一篇)VC到达错误的地方,或者框架尺寸不正确......
对于类按原样,平移导致2移动到3(正确)但是1然后移动到4,视图的大小正确,但似乎偏离预期位置看似任意的距离 . 此后我尝试了各种不同的解决方案 .
在pop部分,我尝试添加以下行(在代码中注释): -
toViewController.view.frame = transitionContext.finalFrame(for: toViewController)
......但VC现在最终收缩(1移至5) . 我希望有人能看到我正在犯的可能是愚蠢的错误 . 我试过简单地将推送部分复制到弹出部分(并反转一切),但它只是不起作用!
仅供参考...那些需要知道如何将过渡连接到UINavigationController的人 - 将UINavigationControllerDelegate添加到导航控制器,以及以下功能......
func navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationControllerOperation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
let transition: SwingTransition = SwingTransition.init(isPresenting: ( operation == .push ? true : false ))
return transition;
}
下图显示了所有视图如何共享相同的起始点(用于转换) . 目的是给出一个左轮手枪枪管的幻觉,将每个VC移动到视野中 . 顶部中心视图表示查看窗口,显示堆栈中的第三个视图 . 为可怜的视觉效果道歉......
2 回答
问题是还原的视图控制器中的某个属性正确重置 . 我想保留非标准的
transform
和anchorPoint
,以防你以后做其他动画假设视图没有被转换) . 因此,在动画的completion
块中,重置视图的position
,anchorPoint
和transform
.当我在这里时,我做了一些其他的改变:
消除了分号;
消除了其中一个持续时间属性;
将
animate
方法的completion
闭包的参数名称修改为finished
而不是animated
以更准确地反映它的真正目的是什么......你也可以使用_
;根据动画是否被取消设置
completeTransition
(因为如果你曾经使这个交互/取消,你不想总是使用true
);使用
.pi
而不是M_PI
;我评论了我对
opacity
的调整,但我通常这样做是为了让效果更加润滑,并确保如果你调整角度使视图重叠,你实际上不需要实际计算参数,所以必须使用's no overlapping, regardless of screen dimensions, so that wasn'注释掉opacity
行,但您可以考虑使用它们,具体取决于所需的效果 .之前我展示了如何简化过程,但结果效果并不完全符合您的要求,但如果您有兴趣,请参阅previous rendition of this answer .
当您执行自定义视图控制器转换时,您的问题是常见问题 . 我知道这是因为我做了很多:)
您正在寻找弹出过渡中的问题,但实际问题在于推动 . 如果在转换后检查堆栈中第一个控制器的视图,您将看到它有一个不寻常的框架,因为您已经弄乱了它的变换和锚点以及图层位置等等 . 真的,你需要在结束过渡之前清理所有这些,否则它会在你以后咬你,就像你在流行音乐中看到的那样 .
一种更简单,更安全的自定义转换方法是添加一个"canvas"视图,然后在该画布上添加传出和传入视图的快照,并对其进行操作 . 这意味着您在转换结束时没有清理 - 只需删除画布视图 . 我写过这个技术here . 对于您的情况,我添加了以下便捷方法:
然后更新您的转换代码以在画布上移动快照改为:
这个特定的转换非常简单,因此重置视图框架的属性并不困难,但如果你做了更复杂的事情,那么画布和快照方法的效果非常好,所以我倾向于在任何地方使用它 .