簡體   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