自定义UIViewController动画问题

3
我使用UIViewControllerAnimatedTransitioningUIViewController创建了一个简单的自定义过渡动画。虽然一切都正常,但是当目标视图呈现时,内容从右下角开始出现,当视图消失时,内容从左上角消失。我附上了一个带有示例的gif。这是我的代码:

CustomViewAnimator.swift

class CustomViewAnimator: NSObject, UIViewControllerAnimatedTransitioning {

    private let interval = 0.5
    var forward = false
    var originFrame: CGRect = .zero

    func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
        return interval
    }

    func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {

        let containerView = transitionContext.containerView

        guard let toView = transitionContext.view(forKey: .to) else {return}
        guard let fromView = transitionContext.view(forKey: .from) else {return}
        let destinationView = forward ? toView : fromView
        destinationView.alpha = forward ? 0 : 1

        forward ? containerView.addSubview(toView) : containerView.insertSubview(toView, belowSubview: fromView)
        forward ? (destinationView.frame = originFrame) : (destinationView.frame = fromView.frame)

        UIView.animate(withDuration: interval, animations: {

            self.forward ? (destinationView.frame = fromView.frame) : (destinationView.frame = self.originFrame)
            destinationView.alpha = self.forward ? 1 : 0

        }) { (finished) in
            transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
        }

    }

}

MainViewController.self

class MainViewController: UIViewController {

    private var button: UIButton!

    override func viewDidLoad() {
        super.viewDidLoad()
        setup()
    }

    @objc private func openView() {
        let viewController = TargetViewController()
        viewController.transitioningDelegate = self
        navigationController?.pushViewController(viewController, animated: true)

    }
}

extension MainViewController: UINavigationControllerDelegate {
    func navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationController.Operation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {

        let animator = CustomViewAnimator()
        animator.originFrame = button.frame
        animator.forward = (operation == .push)
        return animator
    }
}

extension MainViewController: UIViewControllerTransitioningDelegate {
    func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        let animator = CustomViewAnimator()
        animator.forward = true
        return animator
    }

    func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        let animator = CustomViewAnimator()
        animator.forward = false
        return animator
    }
}

TargetViewController.swift

class TargetViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        setup()
    }

}

extension TargetViewController {

    fileprivate func setup() {
        view.backgroundColor = .orange
        navigationItem.title = "Target view"

        let label: UILabel = {
            let label = UILabel()
            label.translatesAutoresizingMaskIntoConstraints = false
            label.text = "Hello from target view :)"
            label.textColor = .white
            label.font = UIFont.boldSystemFont(ofSize: 18)
            return label
        }()
        view.addSubview(label)

        label.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
        label.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
    }

}

有人能否说明如何解决这个问题,使得TargetViewController中的标签(或任何其他内容)始终固定在它们的位置上?

enter image description here


你能展示一下TargetViewController,特别是它的标签约束吗? - Peter Tretyakov
@PeterTretyakov 我编辑了我的帖子并添加了TargetViewController。 - Jack Daniel
2个回答

1
基本上,您的问题是由于混合使用了自动布局和框架。您在ViewController的视图中使用约束来定位子视图(按钮和标签),但是对于动画,您使用框架更新。因此,根据我正确理解布局的方式,您的标签约束在转换期间不活动,或者至少不被布局引擎用于计算标签的位置。
因此,最好的建议是避免在转换中使用框架动画,并尝试为.from.to视图创建适当的约束,并动画化约束的常量。这需要更多的代码,但您可以确保不混合两种不同的布局策略。
更简单的解决方案是告诉containerView在动画期间计算约束并适当地定位元素,只需将containerView.layoutIfNeeded()添加到动画块中即可。所以它会变成这样:
UIView.animate(withDuration: interval, animations: {
    self.forward ? (destinationView.frame = fromView.frame) : (destinationView.frame = self.originFrame)
    destinationView.alpha = self.forward ? 1 : 0
    containerView.layoutIfNeeded()
}) { (finished) in
    transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
}

但这只是让事情工作起来,并不能解决同时使用两种布局策略的主要问题。

是的,我在另一个资源中找到了这个问题。我使用CABasicAnimation来修复它,而不是UIView.animation。 - Jack Daniel

1
我同意其他回答中的讨论。我的建议是,不要动画化UIView的框架,而是通过以下方式动画化目标视图的掩码。这应该是操作者真正想要的效果。
粗体部分是与原帖不同的地方。
 func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {

    let containerView = transitionContext.containerView

    guard let toView = transitionContext.view(forKey: .to) else {return}
    guard let fromView = transitionContext.view(forKey: .from) else {return}
    let destinationView = forward ? toView : fromView
    destinationView.alpha = forward ? 0 : 1

让maskView等于UIView.init(frame: forward ? originFrame : fromView.frame)

将maskView的背景颜色设置为UIColor.white

将destinationView的遮罩设置为maskView

    forward ? containerView.addSubview(toView) : containerView.insertSubview(toView, belowSubview: fromView)
    forward ? (destinationView.frame = toView.frame) : (destinationView.frame = fromView.frame)


    UIView.animate(withDuration: interval, animations: {

self.forward ? (destinationView.mask?.frame = fromView.frame) : (destinationView.mask?.frame = self.originFrame)

这段代码涉及到编程,意思是根据条件判断,将目标视图的遮罩框架设置为源视图的框架或者是自身原始框架。请注意保留HTML标签。
        destinationView.alpha = self.forward ? 1 : 0
            }) { (finished) in

        transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
    }

}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接