Cómo presentar el controlador de vista de derecha a izquierda en iOS usando Swift

82

Estoy usando presentViewController para presentar una nueva pantalla

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

Esto presenta una nueva pantalla de abajo hacia arriba, pero quiero que se presente de derecha a izquierda sin usar UINavigationController.

Estoy usando Xib en lugar del guión gráfico, ¿cómo puedo hacer eso?

Umair Afzal
fuente
en resumen, respuesta completa: stackoverflow.com/a/48081504/294884
Fattie

Respuestas:

158

No importa si lo es xibo lo storyboardque estás usando. Normalmente, la transición de derecha a izquierda se usa cuando empuja un controlador de vista al de presentador UINavigiationController.

ACTUALIZAR

Función de sincronización agregada kCAMediaTimingFunctionEaseInEaseOut

Proyecto de muestra con implementación de Swift 4 agregada a GitHub


Swift 3 y 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];


Rápido 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)

Parece que el animatedparámetro en el presentViewControllermétodo realmente no importa en este caso de transición personalizada. Puede tener cualquier valor, ya sea trueo false.

mrvincenzo
fuente
2
Intenté usar el código Swift3 dentro del método perform () de un UIStoryboardSegue personalizado, el problema es que la transición se realiza en la misma vista y luego aparece la segunda vista. ¿Alguna idea sobre eso?
Devarshi
2
uUn momento, cuando uso este método, el VC de origen se está desvaneciendo. ¿Cómo puedo eliminar ese efecto de atenuación y hacer que sea como la animación de inserción?
Umair Afzal
1
@UmairAfzal Ese es el color de la ventana. Necesitas cambiar el color de la ventana para evitarlo.
Abdul Rehman
Cómo usar esta transición de manera efectiva con UIModalPresentationStyle.overCurrentContext en Swift 3
Mamta
3
Si ve "animado" a "verdadero", también tendrá una ligera animación hacia arriba. Recomiendo configurarlo en "falso"
Rebeloper
66

Código completo para presentar / despedir, 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)
    }
}
Дмитрий Боровиков
fuente
1
Por favor sé más específico. Explica más !
Emm
2
Votar a favor su respuesta porque también tiene la función de descarte
Rebeloper
¡¡Uno de los mejores que hay!! 👌
Shyam
42

Lee todas las respuestas y no veo la solución correcta. La forma correcta de hacerlo es hacer UIViewControllerAnimatedTransitioning personalizado para el delegado de VC presentado.

Por lo tanto, se supone que se deben realizar más pasos, pero el resultado es más personalizable y no tiene efectos secundarios, como pasar de la vista junto con la vista presentada.

Entonces, suponga que tiene algún ViewController, y hay un método para presentar

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

presentTransitiony dismissTransitionse utiliza para animar sus controladores de vista. Entonces adopta su ViewController para UIViewControllerTransitioningDelegate:

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

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

Entonces, el último paso es crear su transición personalizada:

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

En ese controlador de vista de código que se presenta sobre el contexto actual, puede realizar sus personalizaciones desde ese punto. También puede ver que la costumbre también UIPresentationControlleres útil (pase en el uso UIViewControllerTransitioningDelegate)

HotJard
fuente
¡Bien hecho! Estuve probando todas las respuestas por un tiempo y había algo que no estaba bien ... Incluso voté a favor de Uno que no debería haber sido ...
Reimond Hill
¿Cuándo descarta el VC presentado, simplemente llama descartar (animado: verdadero, finalización: nulo) ???
Reimond Hill
@ReimondHill sí, solo estoy usando un sistema de control de vista ordinario
HotJard
Esta es, con mucho, la mejor solución. Sí, es un poco más complicado, pero es robusto y no se romperá bajo diversas condiciones. La respuesta aceptada es un truco.
mmh02
Funciona perfectamente en todos los dispositivos excepto iPhone 7+, 8+, X, XMax. ¿Alguna idea de por qué? la vc no está tomando el fotograma completo.
Umair Afzal
14

También puede utilizar segue personalizado.

Rápido 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)
        })
    }
}
Punto de fotón
fuente
Mejor respuesta que otra si se usa en la presentación sobre el contexto actual
Varun Naharia
Al descartar este segue personalizado, sigue teniendo como valor predeterminado el tipo de segue original. ¿Hay alguna forma de evitar eso?
Alex Bailey
1
mientras que las segues personalizadas son geniales, son solo una solución de guión gráfico
Fattie
7

Prueba esto,

    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)

Aquí vcestá viewcontroller, dashboardWorkouten su caso.

Ketan Parmar
fuente
3
Gracias, pero el próximo VC aparece de repente en lugar de aparecer de derecha a izquierda. Cambié la duración de la animación, pero sigue siendo la misma
Umair Afzal
4

Si desea utilizar el método de transición CAT de "solución rápida" ...

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
}

y entonces ...

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
}

Desafortunadamente, todo esto no es realmente correcto :(

CATransition realmente no está hecho para hacer este trabajo.

Tenga en cuenta que obtendrá el molesto fundido cruzado a negro que, lamentablemente, arruina el efecto.


A muchos desarrolladores (como yo) realmente no les gusta usar NavigationController. A menudo, es más flexible presentar simplemente de manera ad-hoc sobre la marcha, especialmente para aplicaciones inusuales y complejas. Sin embargo, no es difícil "agregar" un controlador de navegación.

  1. simplemente en el guión gráfico, vaya a la entrada VC y haga clic en "incrustar -> en el controlador de navegación". Realmente eso es todo. O,

  2. en didFinishLaunchingWithOptionses fácil de construir su controlador de navegación si lo prefiere

  3. realmente ni siquiera necesita mantener la variable en cualquier lugar, ya .navigationControllerque siempre está disponible como propiedad, fácil.

Realmente, una vez que tienes un navigationController, es trivial hacer transiciones entre pantallas,

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

y tu puedes pop .

Sin embargo, eso solo le da el efecto estándar de "doble empuje" de Apple ...

(El viejo se desliza a menor velocidad, mientras que el nuevo se desliza).

En general, y sorprendentemente, normalmente debe hacer el esfuerzo de realizar una transición personalizada completa.

Incluso si solo desea la transición más simple, más común, pasar / salir, tiene que hacer una transición personalizada completa.

Afortunadamente, para hacer eso, hay un código repetitivo de cortar y pegar en este control de calidad ... https://stackoverflow.com/a/48081504/294884 . ¡Feliz año nuevo 2018!

Fattie
fuente
3

importar UIKit y crear una extensión para 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)
}}

después de una llamada simple:

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

de izquierda a derecha:

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

puede cambiar la duración con su duración preferida ...

Fabio
fuente
1

Prueba esto.

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)
Ashwin Felix
fuente
1
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)
Chile
fuente
Agregué esto en mi evento de clic de botón para animación. Me funciona en Xcode 10.2 swift 4.
Chilli
0

Presentar el controlador de vista de derecha a izquierda en iOS usando 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"
}

Referencia de: [ https://developer.apple.com/documentation/quartzcore/catransition][1]

FURKAN VIJAPURA
fuente
-1

Tengo una mejor solución que me ha funcionado si quieres mantener la animación clásica de Apple, sin ver esa transición "negra" que ves usando CATransition (). Simplemente use el método popToViewController.

Puede buscar el viewController que necesita en

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

Puede buscar el viewController que necesita buscando su restoreIdentifier.

TENGA CUIDADO: por ejemplo, si está navegando a través de 3 controles de vista, y cuando llega al último, desea abrir el primero. En este caso, perderá la referencia al SEGUNDO controlador de vista efectivo porque el popToViewController lo ha sobrescrito. Por cierto, hay una solución: puede guardar fácilmente antes del popToViewcontroller, el VC que necesitará más adelante. Me funcionó muy bien.

Edoardo Vicoli
fuente
-4

Esto funcionó para mí:

self.navigationController?.pushViewController(controller, animated: true)
Mathis Delaunay
fuente
No quería incluirlo en la pila de navegación.
Umair Afzal