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

时间:2016-06-09 09:42:47

标签: ios swift swift2 segue

我正在使用presentViewController来呈现新屏幕

let dashboardWorkout = DashboardWorkoutViewController()
presentViewController(dashboardWorkout, animated: true, completion: nil)

这显示了从下到上的新屏幕,但我希望它在不使用UINavigationController的情况下从右到左呈现。

我使用Xib而不是故事板,所以我该怎么做?

14 个答案:

答案 0 :(得分:127)

如果您使用的是xibstoryboard,则无关紧要。通常,当您将视图控制器推送到提交者的UINavigiationController时,会使用从右到左的转换。

<强>更新

添加了计时功能kCAMediaTimingFunctionEaseInEaseOut

Swift 4 实施添加到GitHub

Sample project


Swift 3&amp; 4.2

let transition = CATransition()
transition.duration = 0.5
transition.type = CATransitionType.push
transition.subtype = CATransitionSubtype.fromRight
transition.timingFunction = CAMediaTimingFunction(name:CAMediaTimingFunctionName.easeInEaseOut)
view.window!.layer.add(transition, forKey: kCATransition)
present(dashboardWorkout, animated: false, completion: nil)


ObjC

CATransition *transition = [[CATransition alloc] init];
transition.duration = 0.5;
transition.type = kCATransitionPush;
transition.subtype = kCATransitionFromRight;
[transition setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]];
[self.view.window.layer addAnimation:transition forKey:kCATransition];
[self presentViewController:dashboardWorkout animated:false completion:nil];


Swift 2.x

let transition = CATransition()
transition.duration = 0.5
transition.type = kCATransitionPush
transition.subtype = kCATransitionFromRight
transition.timingFunction = CAMediaTimingFunction(name:kCAMediaTimingFunctionEaseInEaseOut)
view.window!.layer.addAnimation(transition, forKey: kCATransition)
presentViewController(dashboardWorkout, animated: false, completion: nil)

似乎animated方法中的presentViewController参数在这种自定义转换的情况下并不重要。它可以是任何值,truefalse

答案 1 :(得分:40)

完整的现在/解雇代码,Swift 3

extension UIViewController {

    func presentDetail(_ viewControllerToPresent: UIViewController) {
        let transition = CATransition()
        transition.duration = 0.25
        transition.type = kCATransitionPush
        transition.subtype = kCATransitionFromRight
        self.view.window!.layer.add(transition, forKey: kCATransition)

        present(viewControllerToPresent, animated: false)
    }

    func dismissDetail() {
        let transition = CATransition()
        transition.duration = 0.25
        transition.type = kCATransitionPush
        transition.subtype = kCATransitionFromLeft
        self.view.window!.layer.add(transition, forKey: kCATransition)

        dismiss(animated: false)
    }
}

答案 2 :(得分:16)

阅读所有答案,但无法找到正确的解决方案。正确的方法是为呈现的VC委托制作自定义UIViewControllerAnimatedTransitioning。

因此,它假定采取更多步骤,但结果更具可定制性,并且避免了一些副作用,例如从视图移动到现有视图。

所以,假设你有一些ViewController,并且有一种方法来呈现

var presentTransition: UIViewControllerAnimatedTransitioning?
var dismissTransition: UIViewControllerAnimatedTransitioning?    

func showSettings(animated: Bool) {
    let vc = ... create new vc to present

    presentTransition = RightToLeftTransition()
    dismissTransition = LeftToRightTransition()

    vc.modalPresentationStyle = .custom
    vc.transitioningDelegate = self

    present(vc, animated: true, completion: { [weak self] in
        self?.presentTransition = nil
    })
}

presentTransitiondismissTransition用于为视图控制器设置动画。 因此,您将ViewController采用到UIViewControllerTransitioningDelegate

extension ViewController: UIViewControllerTransitioningDelegate {
    func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        return presentTransition
    }

    func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        return dismissTransition
    }
}

所以最后一步是创建自定义转换:

class RightToLeftTransition: NSObject, UIViewControllerAnimatedTransitioning {
    let duration: TimeInterval = 0.25

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

    func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
        let container = transitionContext.containerView
        let toView = transitionContext.view(forKey: .to)!

        container.addSubview(toView)
        toView.frame.origin = CGPoint(x: toView.frame.width, y: 0)

        UIView.animate(withDuration: duration, delay: 0, options: .curveEaseOut, animations: {
            toView.frame.origin = CGPoint(x: 0, y: 0)
        }, completion: { _ in
            transitionContext.completeTransition(true)
        })
    }
}

class LeftToRightTransition: NSObject, UIViewControllerAnimatedTransitioning {
    let duration: TimeInterval = 0.25

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

    func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
        let container = transitionContext.containerView
        let fromView = transitionContext.view(forKey: .from)!

        container.addSubview(fromView)
        fromView.frame.origin = .zero

        UIView.animate(withDuration: duration, delay: 0, options: .curveEaseIn, animations: {
            fromView.frame.origin = CGPoint(x: fromView.frame.width, y: 0)
        }, completion: { _ in
            fromView.removeFromSuperview()
            transitionContext.completeTransition(true)
        })
    }
}

在该代码视图控制器中显示当前上下文,您可以从该点进行自定义。您也可以看到自定义UIPresentationController也很有用(使用UIViewControllerTransitioningDelegate传递)

答案 3 :(得分:13)

您也可以使用自定义segue。

Swift 3

class SegueFromRight: UIStoryboardSegue
{ 
    override func perform()
    {
    let src = self.source
    let dst = self.destination

    src.view.superview?.insertSubview(dst.view, aboveSubview: src.view)
    dst.view.transform = CGAffineTransform(translationX: src.view.frame.size.width, y: 0)

    UIView.animate(withDuration: 0.25,
                   delay: 0.0,
                   options: UIViewAnimationOptions.curveEaseInOut,
                   animations: {
                    dst.view.transform = CGAffineTransform(translationX: 0, y: 0)
    },
                   completion: { finished in
                    src.present(dst, animated: false, completion: nil)
    }
    )
}
}

答案 4 :(得分:6)

试试这个,

    let animation = CATransition()
    animation.duration = 0.5
    animation.type = kCATransitionPush
    animation.subtype = kCATransitionFromRight
     animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
    vc.view.layer.addAnimation(animation, forKey: "SwitchToView")

    self.presentViewController(vc, animated: false, completion: nil)

此处vc是viewcontroller,在您的情况下为dashboardWorkout

答案 5 :(得分:4)

如果您确实想使用&#34;快速修复&#34; CATransition方法....

class AA: UIViewController

 func goToBB {

    let bb = .. instantiateViewcontroller, storyboard etc .. as! AlreadyOnboardLogin

    let tr = CATransition()
    tr.duration = 0.25
    tr.type = kCATransitionMoveIn // use "MoveIn" here
    tr.subtype = kCATransitionFromRight
    view.window!.layer.add(tr, forKey: kCATransition)

    present(bb, animated: false)
    bb.delegate, etc = set any other needed values
}

然后......

func dismissingBB() {

    let tr = CATransition()
    tr.duration = 0.25
    tr.type = kCATransitionReveal // use "Reveal" here
    tr.subtype = kCATransitionFromLeft
    view.window!.layer.add(tr, forKey: kCATransition)

    dismiss(self) .. or dismiss(bb), or whatever
}

遗憾的是,所有这一切都不正确:(

CATransition并非真正为这项工作做好准备。

请注意,你会得到令人讨厌的交叉渐变为黑色,但遗憾的是这会破坏效果。

许多开发者(像我一样)真的不喜欢使用NavigationController。通常情况下,您可以更灵活地以特别的方式呈现,特别是对于不寻常和复杂的应用程序。但是,添加&#34;并不困难。导航控制器。

  1. 只需在故事板上,转到条目VC并点击&#34;嵌入 - &gt;在导航控制器&#34;。真的是这样的。或者,

  2. {li>

    didFinishLaunchingWithOptions如果您愿意,可以轻松构建导航控制器

  3. 您甚至不需要将变量保留在任何位置,因为.navigationController始终可用作属性 - 很容易。

  4. 真的,一旦你有了一个navigationController,在屏幕之间进行转换是微不足道的,

        let nextScreen = instantiateViewController etc as! NextScreen
        navigationController?
            .pushViewController(nextScreen, animated: true)
    

    你可以pop

    然而,这只会给你标准的苹果&#34;双推&#34;效果...

    (旧的滑板以较低的速度滑落,因为新的滑动。)

    一般而且令人惊讶的是,您通常必须努力进行完全自定义转换。

    即使您只想要最简单,最常见的移动/移动过渡,您也必须进行完全自定义过渡。

    幸运的是,要做到这一点,在此质量保证上有一些剪切和粘贴样板代码...... https://stackoverflow.com/a/48081504/294884。 新年快乐2018年!

答案 6 :(得分:1)

试试这个。

let transition: CATransition = CATransition()
transition.duration = 0.3

transition.type = kCATransitionReveal
transition.subtype = kCATransitionFromLeft
self.view.window!.layer.addAnimation(transition, forKey: nil)
self.dismissViewControllerAnimated(false, completion: nil)

答案 7 :(得分:0)

使用Swift

在iOS中从右到左显示视图控制器
func FrkTransition() 

{
    let transition = CATransition()

    transition.duration = 2

    transition.type = kCATransitionPush

    transitioningLayer.add(transition,forKey: "transition")

    // Transition to "blue" state

    transitioningLayer.backgroundColor = UIColor.blue.cgColor
    transitioningLayer.string = "Blue"
}

参与: [https://developer.apple.com/documentation/quartzcore/catransition][1]

答案 8 :(得分:0)

let transition = CATransition()
    transition.duration = 0.25
    transition.type = kCATransitionPush
    transition.subtype = kCATransitionFromLeft
    transition.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear)
    tabBarController?.view.layer.add(transition, forKey: kCATransition)
    self.navigationController?.popToRootViewController(animated: true)

答案 9 :(得分:0)

导入UIKit并为UIViewController创建一个扩展:

extension UIViewController {
func transitionVc(vc: UIViewController, duration: CFTimeInterval, type: CATransitionSubtype) {
    let customVcTransition = vc
    let transition = CATransition()
    transition.duration = duration
    transition.type = CATransitionType.push
    transition.subtype = type
    transition.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)
    view.window!.layer.add(transition, forKey: kCATransition)
    present(customVcTransition, animated: false, completion: nil)
}}

简单通话后:

let vC = YourViewController()
transitionVc(vc: vC, duration: 0.5, type: .fromRight)

从左到右:

let vC = YourViewController()
transitionVc(vc: vC, duration: 0.5, type: .fromleft)

您可以使用所需的持续时间更改持续时间...

答案 10 :(得分:0)

    // Never Easy than this before :)
     // you just need to add a Static function for navigation transition
     // This Code is recommended for the view controller.
  
     public class CustomNavigation:  UIViewController {
            
            
            public override func loadView() {
                super.loadView();
                
            }
            public override func viewDidLoad() {
                super.viewDidLoad()
                // Do any additional setup after loading the view.
            }
            
            public override func viewDidAppear(_ animated: Bool) {
                super.viewDidAppear(true);
            }
        
        public static func segueNavToLeft(view: UIView) {
                let transition = CATransition()
                transition.duration = 0.3
                transition.type = CATransitionType.push
                transition.subtype = CATransitionSubtype.fromLeft
                transition.timingFunction = CAMediaTimingFunction(name:CAMediaTimingFunctionName.easeInEaseOut)
                view.window!.layer.add(transition, forKey: kCATransition)
            }
            
            public static func segueNavToRight(view: UIView) {
                let transition = CATransition()
                transition.duration = 0.3
                transition.type = CATransitionType.push
                transition.subtype = CATransitionSubtype.fromRight
                transition.timingFunction = CAMediaTimingFunction(name:CAMediaTimingFunctionName.easeInEaseOut)
                view.window!.layer.add(transition, forKey: kCATransition)
            }
            
        }
    
    
    
    // simply call in your viewcontroller:
     func moveToRight()  {
            
            CustomNavigation.segueNavToRight(view: view)
    
            let controller = self.storyboard?.instantiateViewController(withIdentifier: "id") as! YourViewController
            let navigationController = UINavigationController(rootViewController: YourViewController)
            navigationController.modalPresentationStyle = .fullScreen
            self.present(navigationController, animated: false, completion: nil)
            
          
        }

func moveToLeft() {

       CustomNavigation.segueNavToLeft(view: view)
        self.dismiss(animated: true, completion: nil)
} 

答案 11 :(得分:0)

  1. 推送时为灰色背景
class RightToLeftPresentationController: UIPresentationController {
    lazy var blackView: UIView = {
        let view = UIView()
        view.frame = self.containerView?.bounds ?? .zero
        view.backgroundColor = #colorLiteral(red: 0, green: 0, blue: 0, alpha: 0.5)
        
        return view
    }()
    
    // MARK:- Presentation
    override func presentationTransitionWillBegin() {
        self.containerView?.addSubview(blackView)
        
        self.presentingViewController.transitionCoordinator?
            .animate(alongsideTransition: { _ in
                self.blackView.alpha = 0.5
            }, completion: nil)
    }
    
    // MARK:- Dismiss
    override func dismissalTransitionWillBegin() {
        UIView.animate(withDuration: 0.25) {
            self.blackView.alpha = 0
        }
    }

    override func dismissalTransitionDidEnd(_ completed: Bool) {
        if completed {
            blackView.removeFromSuperview()
        }
    }
}
  1. 支持故事板
class RightToLeftPresentationSegue: UIStoryboardSegue {
    override func perform() {
        destination.modalPresentationStyle = .custom
        destination.transitioningDelegate = self
        source.present(destination, animated: true)
    }
}
extension RightToLeftPresentationSegue: UIViewControllerTransitioningDelegate {
    func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? {
        let controller = RightToLeftPresentationController(presentedViewController: presented, presenting: presenting)
        presented.transitioningDelegate = controller
        
        return controller
    }
}
  1. 支持解雇(感谢@hotjard
extension RightToLeftPresentationController: UIViewControllerTransitioningDelegate {
    func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        return RightToLeftTransition()
    }
    
    func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        return LeftToRightTransition()
    }
}
class RightToLeftTransition: NSObject, UIViewControllerAnimatedTransitioning {
    let duration: TimeInterval = 0.25

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

    func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
        let container = transitionContext.containerView
        let toView = transitionContext.view(forKey: .to)!

        container.addSubview(toView)
        toView.frame.origin = CGPoint(x: toView.frame.width, y: 0)

        UIView.animate(withDuration: duration, delay: 0, options: .curveEaseOut, animations: {
            toView.frame.origin = CGPoint(x: 0, y: 0)
        }, completion: { _ in
            transitionContext.completeTransition(true)
        })
    }
}

class LeftToRightTransition: NSObject, UIViewControllerAnimatedTransitioning {
    let duration: TimeInterval = 0.25

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

    func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
        let container = transitionContext.containerView
        let fromView = transitionContext.view(forKey: .from)!

        container.addSubview(fromView)
        fromView.frame.origin = .zero

        UIView.animate(withDuration: duration, delay: 0, options: .curveEaseIn, animations: {
            fromView.frame.origin = CGPoint(x: fromView.frame.width, y: 0)
        }, completion: { _ in
            fromView.removeFromSuperview()
            transitionContext.completeTransition(true)
        })
    }
}

答案 12 :(得分:-1)

如果您想要保留Apple的经典动画,我有一个更好的解决方案对我有用,而不会看到您使用CATransition()看到的“黑色”转换。只需使用popToViewController方法。

您可以在

中查找所需的viewController
self.navigationController.viewControllers // Array of VC that contains all VC of current stack

您可以通过搜索它的restorationIdentifier来查找所需的viewController。

小心:例如,如果您正在浏览3个视图控制器,当到达最后一个时,您想要弹出第一个视图控制器。在这种情况下,您将失去对有效SECOND视图控制器的引用,因为popToViewController已覆盖它。顺便说一下,有一个解决方案:你可以在popToViewcontroller之前轻松保存,以后你需要VC。它非常适合我。

答案 13 :(得分:-3)

这对我有用:

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