【问题标题】:How to present view controller from right to left in iOS using Swift如何使用 Swift 在 iOS 中从右到左呈现视图控制器
【发布时间】:2016-10-09 21:24:15
【问题描述】:

我正在使用 presentViewController 来展示新屏幕

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

这会从下到上呈现新屏幕,但我希望它从右到左呈现而不使用UINavigationController

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

【问题讨论】:

标签: ios swift swift2 segue


【解决方案1】:

您使用的是xib 还是storyboard 都没有关系。通常,当您将视图控制器推送到演示者的UINavigiationController 时,会使用从右到左的过渡。

更新

新增计时功能kCAMediaTimingFunctionEaseInEaseOut

Sample project 已将 Swift 4 实现添加到 GitHub


Swift 3 和 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)

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

【讨论】:

  • 我尝试在自定义 UIStoryboardSegue 的 perform() 方法中使用 Swift3 代码,问题是 - 转换是在同一个视图上完成的,然后出现第二个视图。有什么想法吗?
  • uUrrently 当我使用此方法时,源 VC 正在淡出我如何才能删除该淡入淡出效果并使其就像推送动画一样?
  • @UmairAfzal 这就是窗口的颜色,您需要更改窗口的颜色以避免这种情况。
  • 如何在 Swift 3 中通过 UIModalPresentationStyle.overCurrentContext 有效地使用这个过渡
  • 如果您将“动画”设置为“真”,它也会有轻微的向上动画。我建议将其设置为“false”
【解决方案2】:

出席/解雇的完整代码,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)
    }
}

【讨论】:

  • 请更具体。解释更多!
  • 支持你的答案,因为它也有关闭功能
  • 最好的之一!! ?
【解决方案3】:

阅读所有答案,但看不到正确的解决方案。这样做的正确方法是为呈现的 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 传递)

【讨论】:

  • 干得好!我尝试了一段时间的所有答案,但有些地方不对劲......我什至投票给了一个不应该......
  • 你什么时候解散提供的VC,你只是调用dismiss(animated:true,completion:nil)???
  • @ReimondHill 是的,我只是使用普通的dismissViewController
  • 这是迄今为止最好的解决方案。是的,它有点复杂,但它很坚固,在各种条件下都不会损坏。接受的答案是 hack。
  • 它可以在除 iPhone 7+、8+、X、XMax 之外的所有设备上完美运行。知道为什么吗? vc 没有占用全帧。
【解决方案4】:

您也可以使用自定义转场。

斯威夫特 5

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: UIView.AnimationOptions.curveEaseInOut,
               animations: {
                    dst.view.transform = CGAffineTransform(translationX: 0, y: 0)
            },
                   completion: { finished in
                    src.present(dst, animated: false, completion: nil)
        })
    }
}

【讨论】:

  • 如果在当前上下文的演示中使用,则比其他答案更好
  • 当关闭此自定义 segue 时,它​​仍默认为原始 segue 类型。有办法解决吗?
  • 虽然自定义转场很棒 - 它们只是故事板解决方案
【解决方案5】:

试试这个,

    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 是视图控制器,dashboardWorkout 在你的情况下。

【讨论】:

  • 谢谢,但是下一个 VC 是突然出现的,而不是从右到左出现。我改变了动画持续时间,但还是一样
【解决方案6】:

导入 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)
}}

simlpy 调用后:

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

从左到右:

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

您可以使用您喜欢的持续时间更改持续时间...

【讨论】:

    【解决方案7】:

    如果您确实想使用“快速修复”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。通常,在您进行时以临时方式呈现会更加灵活,特别是对于不寻常和复杂的应用程序。但是,“添加”一个导航控制器并不难。

    1. 只需在故事板上,转到条目 VC 并单击“嵌入 -> 在导航控制器中”。真的就是这样。

    或者,如果你愿意的话

    1. didFinishLaunchingWithOptions 中很容易在代码中添加导航控制器

    2. 您甚至不需要将变量保存在任何地方,因为 .navigationController 始终可以作为属性使用 - 很简单。

    真的,一旦你有了一个导航控制器,在屏幕之间进行转换就很简单了,

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

    你可以pop

    还有一个问题!然而,这只会给你标准的苹果“双推”效果......

    (旧的以较低的速度滑下,而新的滑上。)

    通常令人惊讶的是,您通常必须努力进行完整的自定义过渡。

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

    幸运的是,为此 QA 有一些剪切和粘贴样板代码...https://stackoverflow.com/a/48081504/294884。 2018 年新年快乐!

    【讨论】:

      【解决方案8】:

      试试这个。

      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)
      

      【讨论】:

        【解决方案9】:
        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)
        

        【讨论】:

        • 我在我的按钮点击事件中添加了这个动画。它适用于我在 Xcode 10.2 swift 4 中。
        【解决方案10】:

        使用 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]

        【讨论】:

          【解决方案11】:
              // 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)
          } 
          

          【讨论】:

            【解决方案12】:
            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)
                    })
                }
            }
            

            【讨论】:

              【解决方案13】:

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

              你可以在里面寻找你需要的viewController

              self.navigationController.viewControllers // Array of VC that contains all VC of current stack
              

              你可以通过搜索它的restoreIdentifier来找到你需要的viewController。

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

              【讨论】:

                【解决方案14】:

                这对我有用:

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

                【讨论】:

                • 我不想把它推到导航堆栈中。
                猜你喜欢
                • 2019-01-11
                • 1970-01-01
                • 2015-02-27
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2018-11-09
                • 2014-10-09
                • 2015-11-18
                相关资源
                最近更新 更多