Как представить контроллер вида справа налево в iOS с помощью Swift

Я использую presentViewController для представления нового экрана

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

Это представляет новый экран снизу вверх, но я хочу, чтобы он отображался справа налево без использования UINavigationController,

Я использую Xib вместо раскадровки, так как я могу это сделать?

14 ответов

Решение

Неважно, если это xib или же storyboard что вы используете. Обычно переход справа налево используется, когда вы помещаете контроллер представления в UINavigiationController,

ОБНОВИТЬ

Добавлена ​​функция синхронизации kCAMediaTimingFunctionEaseInEaseOut

Пример проекта с реализацией Swift 4 добавлен в GitHub


Свифт 3 и 4

let transition = CATransition()
transition.duration = 0.5
transition.type = kCATransitionPush
transition.subtype = kCATransitionFromRight
transition.timingFunction = CAMediaTimingFunction(name:kCAMediaTimingFunctionEaseInEaseOut)
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 метод не имеет большого значения в этом случае пользовательского перехода. Это может иметь любую ценность, либо true или же false,

Полный код для подарка / отклонения, 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)
    }
}

Прочитайте все ответы и не можете найти правильное решение. Правильный способ сделать это - создать пользовательский UIViewControllerAnimatedTransitioning для представленного делегата VC.

Таким образом, предполагается сделать больше шагов, но результат будет более настраиваемым и не будет иметь побочных эффектов, таких как переход от вида вместе с представленным видом.

Итак, предположим, у вас есть какой-то 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
    })
}

presentTransition а также dismissTransition используется для анимации ваших контроллеров представления. Таким образом, вы принимаете ваш 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

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

Попробуй это,

    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 в твоем случае.

Импортируйте 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)

Вы можете изменить продолжительность с вашей предпочтительной продолжительностью...

Если вы хотите использовать метод быстрого исправления 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 и нажмите "embed -> in nav controller". На самом деле это все. Или же,

  2. в didFinishLaunchingWithOptions легко построить свой навигационный контроллер, если вы предпочитаете

  3. вам даже не нужно хранить переменную где-либо, так как .navigationController всегда доступен как собственность - легко.

Действительно, когда у вас есть навигационный контроллер, тривиально выполнять переходы между экранами,

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

и ты можешь pop,

Это, однако, только дает вам стандартный яблочный эффект "двойного толчка"...

(Старый скользит с меньшей скоростью, а новый скользит.)

Как правило, как ни удивительно, вам обычно приходится прилагать все усилия, чтобы сделать полный пользовательский переход.

Даже если вы просто хотите получить самый простой, самый распространенный переход при переходе / отключении, вы должны выполнить полный пользовательский переход.

К счастью, для этого есть некоторый шаблонный код для этого QA... /questions/17798922/kak-izmenit-animatsiyu-push-i-pop-v-prilozhenii-na-osnove-navigatsii/17798939#17798939. С новым годом 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)
          // Never Easy than this before :)
     // you just need to add a Static function for navigation transition
  
     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)
} 

Представить контроллер вида справа налево в iOS с помощью Swift

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]

  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

Вы можете найти нужный вам viewController, выполнив поиск его restoreIdentifier.

БУДЬТЕ ОСТОРОЖНЫ: например, если вы перемещаетесь по 3 контроллерам вида, и когда вы прибываете в последний раз, вы хотите всплыть первым. В этом случае вы потеряете ссылку на эффективный контроллер представления ВТОРОГО, потому что popToViewController перезаписал его. Кстати, есть решение: вы можете легко сохранить до popToViewcontroller, VC вам понадобится позже. Это сработало для меня очень хорошо.

Это сработало для меня:

self.navigationController?.pushViewController(controller, animated: true)
Другие вопросы по тегам