如何在iOS中从左到右呈现视图控制器?

19

当向导航堆栈中添加新控制器时:

self.navigationController!.pushViewController(PushedViewController(), animated: true)

从右侧出现:

输入图像描述

我该如何改变动画方向,使其从左边出现?


你想要推送(push)还是展示(present) ViewController? - Sanjay Shah
2
提醒一下,这与苹果的设计准则非常不符,可能会让你的用户感到困惑。 - ZGski
8个回答

16

Swift 5.1:从不同方向进行Segue

这是一个用于实现不同Segue方向的简单扩展。(在Swift 5中测试通过)

看起来你想使用segueFromLeft(),我还添加了一些其他示例。

extension CATransition {

//New viewController will appear from bottom of screen.
func segueFromBottom() -> CATransition {
    self.duration = 0.375 //set the duration to whatever you'd like.
    self.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)
    self.type = CATransitionType.moveIn
    self.subtype = CATransitionSubtype.fromTop
    return self
}
//New viewController will appear from top of screen.
func segueFromTop() -> CATransition {
    self.duration = 0.375 //set the duration to whatever you'd like.
    self.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)
    self.type = CATransitionType.moveIn
    self.subtype = CATransitionSubtype.fromBottom
    return self
}
 //New viewController will appear from left side of screen.
func segueFromLeft() -> CATransition {
    self.duration = 0.1 //set the duration to whatever you'd like.
    self.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)
    self.type = CATransitionType.moveIn
    self.subtype = CATransitionSubtype.fromLeft
    return self
}
//New viewController will pop from right side of screen.
func popFromRight() -> CATransition {
    self.duration = 0.1 //set the duration to whatever you'd like.
    self.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)
    self.type = CATransitionType.reveal
    self.subtype = CATransitionSubtype.fromRight
    return self
}
//New viewController will appear from left side of screen.
func popFromLeft() -> CATransition {
    self.duration = 0.1 //set the duration to whatever you'd like.
    self.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)
    self.type = CATransitionType.reveal
    self.subtype = CATransitionSubtype.fromLeft
    return self
   }
}

以下是如何实现上述扩展的步骤:

    let nav = self.navigationController //grab an instance of the current navigationController
    DispatchQueue.main.async { //make sure all UI updates are on the main thread.
        nav?.view.layer.add(CATransition().segueFromLeft(), forKey: nil)
        nav?.pushViewController(YourViewController(), animated: false)
    }

1
这很有帮助。我击败了你。 - coders
我已经尝试在游乐场中测试过了,但是出现了“表达式无法解析,未知错误”的提示。 - Le Mot Juiced
1
太好了!谢谢你。但是我有一个问题。如何在这些动画中去除阴影?如果我使用默认的iOS函数pushViewController - 新视图会没有阴影出现。但是如果我使用你的代码,我会看到阴影。 - VyacheslavBakinkskiy

9
let obj = self.storyboard?.instantiateViewController(withIdentifier: "ViewController")as! ViewController
            
let transition:CATransition = CATransition()
transition.duration = 0.3
transition.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut)
transition.type = .push
transition.subtype = .fromLeft
self.navigationController?.view.layer.add(transition, forKey: kCATransition)

self.navigationController?.pushViewController(obj, animated: true)

当您使用popToViewController时,会返回到指定的视图控制器。
 transition.subtype = kCATransitionFromRight

很棒的回答。只是为了更清楚起见,将完整的代码添加到popToViewController部分。你明白我的意思吧,伙计。 - Diego Jiménez
很好的回答。只是为了更清楚起见,在popToViewController部分添加完整的代码。你明白我的意思了吧,伙计。 - undefined

3
这可能会对您有所帮助。
let nextVc  = self.storyboard?.instantiateViewController(withIdentifier: "nextVc")
    let transition = CATransition()
    transition.duration = 0.5
    transition.type = kCATransitionPush
    transition.subtype = kCATransitionFromLeft
    transition.timingFunction = CAMediaTimingFunction(name:kCAMediaTimingFunctionEaseInEaseOut)
    view.window!.layer.add(transition, forKey: kCATransition)
    self.navigationController?.pushViewController(nextVc!, animated: false)

2

好的,这里有一个对你有帮助的即插即用解决方案。添加名为LeftToRightTransitionProxy.swift的文件,并添加以下内容:

import UIKit

final class LeftToRightTransitionProxy: NSObject {

    func setup(with controller: UINavigationController) {
        controller.delegate = self
    }
}

extension LeftToRightTransitionProxy: UINavigationControllerDelegate {

    func navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationControllerOperation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        if operation == .push {
            return AnimationController(direction: .forward)
        } else {
            return AnimationController(direction: .backward)
        }
    }
}

private final class AnimationController: NSObject, UIViewControllerAnimatedTransitioning {

    enum Direction {
        case forward, backward
    }

    let direction: Direction

    init(direction: Direction) {
        self.direction = direction
    }

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

    func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
        guard let toView = transitionContext.view(forKey: .to),
            let fromView = transitionContext.view(forKey: .from) else {
                return
        }

        let container = transitionContext.containerView
        container.addSubview(toView)

        let initialX: CGFloat
        switch direction {
        case .forward: initialX = -fromView.bounds.width
        case .backward: initialX = fromView.bounds.width
        }
        toView.frame = CGRect(origin: CGPoint(x: initialX, y: 0), size: toView.bounds.size)

        let animation: () -> Void = {
            toView.frame = CGRect(origin: .zero, size: toView.bounds.size)
        }
        let completion: (Bool) -> Void = { _ in
            let success = !transitionContext.transitionWasCancelled
            if !success {
                toView.removeFromSuperview()
            }
            transitionContext.completeTransition(success)
        }
        UIView.animate(
            withDuration: transitionDuration(using: transitionContext),
            animations: animation,
            completion: completion
        )
    }
}

以下是如何使用它的方法:

final class ViewController: UIViewController {

    let animationProxy = LeftToRightTransitionProxy()

    override func viewDidLoad() {
        super.viewDidLoad()

        animationProxy.setup(with: navigationController!)
    }
}

此解决方案为前进和后退(push 和 pop)提供动画效果。您可以在 LeftToRightTransitionProxy 类的 navigationController(_:animationControllerFor:from:to:) 方法中控制此行为(只需返回 nil 即可删除动画)。
如果您需要特定子类的 UIViewController 的此行为,请在 navigationController(_:animationControllerFor:from:to:) 方法中放置适当的检查:
func navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationControllerOperation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
    if operation == .push && toVC is DetailViewController {
        return AnimationController(direction: .forward)
    } else if operation == .pop && toVC is ViewController {
        return AnimationController(direction: .backward)
    }
    return nil
}

2

我使用Hero作为解决方案。

import Hero

在你要显示新的UIViewController的地方,将默认动画关闭:

Hero.shared.defaultAnimation = HeroDefaultAnimationType.cover(direction: .right)

还要指定您的UINavigationController将使用Hero库:

self.navigationController?.hero.isEnabled = true

这样,即使您使用标准的pushViewController函数,也将获得预期的结果:

self.navigationController?.pushViewController(vc, animated: true)

enter image description here



1
如果您想学习如何进行自定义转场(即从右到左呈现),那么this是一个非常好的教程,可以帮助您设置它们。
您需要做的关键事情是设置转场代理、自定义呈现控制器和自定义动画控制器。

-1

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