繁体   English   中英

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

[英]How to present view controller from right to left in iOS using Swift

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

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

这从下到上显示了新屏幕,但我希望它从右到左显示而不使用UINavigationController

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

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

更新

添加计时功能kCAMediaTimingFunctionEaseInEaseOut

Swift 4实现添加到 GitHub 的示例项目


斯威夫特 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)


对象

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];


斯威夫特 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

显示/关闭的完整代码,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)
    }
}

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

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

斯威夫特 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)
        })
    }
}

试试这个,

    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

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

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

如果您确实想使用“快速修复”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 年新年快乐!

试试这个。

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)
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)

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

    // 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)
} 
  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)
        })
    }
}

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

您可以在其中查找所需的 viewController

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

您可以通过搜索它的 restoreIdentifier 来查找您需要的 viewController。

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

这对我有用:

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM