So präsentieren Sie den View Controller in iOS mit Swift von rechts nach links

83

Ich benutze presentViewController, um einen neuen Bildschirm zu präsentieren

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

Dies zeigt einen neuen Bildschirm von unten nach oben, aber ich möchte, dass er von rechts nach links ohne Verwendung dargestellt wird UINavigationController.

Ich verwende Xib anstelle von Storyboard. Wie kann ich das tun?

Umair Afzal
quelle
Kurz gesagt, vollständige Antwort: stackoverflow.com/a/48081504/294884
Fattie

Antworten:

159

Es spielt keine Rolle, ob es ist xiboder storyboardob Sie es verwenden. Normalerweise wird der Übergang von rechts nach links verwendet, wenn Sie einen Ansichts-Controller in den Präsentator schieben UINavigiationController.

AKTUALISIEREN

Timing-Funktion hinzugefügt kCAMediaTimingFunctionEaseInEaseOut

Beispielprojekt mit Swift 4- Implementierung zu GitHub hinzugefügt


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)

Scheint, als ob der animatedParameter in der presentViewControllerMethode in diesem Fall eines benutzerdefinierten Übergangs keine Rolle spielt. Es kann einen beliebigen Wert haben, entweder trueoder false.

mrvincenzo
quelle
2
Ich habe versucht, Swift3-Code in der perform () -Methode eines benutzerdefinierten UIStoryboardSegue zu verwenden. Das Problem ist, dass der Übergang in derselben Ansicht erfolgt und dann die zweite Ansicht angezeigt wird. Irgendwelche Ideen dazu?
Devarshi
2
Wenn ich diese Methode verwende, wird die Quell-VC derzeit ausgeblendet. Wie kann ich diesen Überblendungseffekt entfernen und habe ihn genau wie die Push-Animation?
Umair Afzal
1
@UmairAfzal Das ist die Farbe des Fensters. Sie müssen die Farbe des Fensters ändern, um dies zu vermeiden.
Abdul Rehman
Wie man diesen Übergang effektiv mit UIModalPresentationStyle.overCurrentContext in Swift 3
Mamta
3
Wenn Sie "animiert" auf "wahr" setzen, wird auch eine leichte Aufwärtsanimation angezeigt. Ich empfehle es auf "false" zu setzen
Rebeloper
67

Vollständiger Code für Gegenwart / Entlassung, 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)
    }
}
Дмитрий Боровиков
quelle
1
Bitte sei spezifischer. Erkläre mehr !
Emm
2
Upvoting Ihre Antwort, weil es auch die Entlassungsfunktion hat
Rebeloper
Eines der besten da draußen !! 👌
Shyam
44

Lesen Sie alle Antworten durch und sehen Sie keine richtige Lösung. Der richtige Weg, dies zu tun, besteht darin, eine benutzerdefinierte UIViewControllerAnimatedTransitioning für den präsentierten VC-Delegaten zu erstellen.

Es werden also mehr Schritte vorausgesetzt, aber das Ergebnis ist anpassbarer und weist keine Nebenwirkungen auf, z. B. das Verschieben aus der Ansicht zusammen mit der dargestellten Ansicht.

Angenommen, Sie haben einen ViewController und es gibt eine Methode zum Präsentieren

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

presentTransitionund dismissTransitionwird zum Animieren Ihrer View Controller verwendet. Sie übernehmen Ihren ViewController also für UIViewControllerTransitioningDelegate:

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

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

Der letzte Schritt besteht also darin, Ihren benutzerdefinierten Übergang zu erstellen:

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

In diesem Code View Controller, der über den aktuellen Kontext dargestellt wird, können Sie Ihre Anpassungen von diesem Punkt aus vornehmen. Möglicherweise sehen Sie auch, dass Benutzerdefiniert UIPresentationControllernützlich ist (Weitergabe mit UIViewControllerTransitioningDelegate)

HotJard
quelle
Gut gemacht! Ich habe eine Weile alle Antworten ausprobiert und da stimmte etwas nicht ... Ich habe sogar eine positiv bewertet, die nicht hätte sein sollen ...
Reimond Hill
Wann entlassen Sie die präsentierte VC, rufen Sie einfach entlassen auf (animiert: true, Vervollständigung: null) ???
Reimond Hill
@ReimondHill ja, ich benutze nur gewöhnliche EntlassungViewController
HotJard
Dies ist bei weitem die beste Lösung. Ja, es ist etwas komplizierter, aber es ist robust und bricht unter verschiedenen Bedingungen nicht. Die akzeptierte Antwort ist ein Hack.
mmh02
Es funktioniert perfekt auf allen Geräten außer iPhone 7+, 8+, X, XMax. Irgendeine Idee warum? der vc nimmt nicht den vollen rahmen auf.
Umair Afzal
14

Sie können auch benutzerdefinierte Segmente verwenden.

Swift 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)
        })
    }
}
Photonenpunkt
quelle
Besser antworten als andere, wenn in der Präsentation über den aktuellen Kontext verwendet
Varun Naharia
Wenn Sie diesen benutzerdefinierten Segue schließen, wird standardmäßig der ursprüngliche Segue-Typ verwendet. Gibt es einen Weg daran vorbei?
Alex Bailey
1
Während benutzerdefinierte Segmente großartig sind - sie sind nur eine Storyboard-Lösung
Fattie
7

Versuche dies,

    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)

Hier vcist Viewcontroller dashboardWorkoutin Ihrem Fall.

Ketan Parmar
quelle
3
Danke, aber der nächste VC erscheint plötzlich, anstatt von rechts nach links zu erscheinen. Ich habe die Animationsdauer geändert, aber immer noch dieselbe
Umair Afzal
4

Wenn Sie die CATransition-Methode "Quick Fix" verwenden möchten ...

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
}

und dann ...

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
}

All dies ist leider nicht wirklich richtig :(

CATransition ist nicht wirklich für diesen Job gemacht.

Beachten Sie, dass das nervige Kreuz schwarz wird, was den Effekt leider ruiniert.


Viele Entwickler (wie ich) mögen es wirklich nicht NavigationController. Insbesondere bei ungewöhnlichen und komplexen Apps ist es häufig flexibler, sich unterwegs nur ad-hoc zu präsentieren. Es ist jedoch nicht schwierig, einen Navigationscontroller hinzuzufügen.

  1. Gehen Sie einfach im Storyboard zum Eintrag VC und klicken Sie auf "Einbetten -> in Navi-Controller". Wirklich, das war's. Oder,

  2. in didFinishLaunchingWithOptionsist es einfach Ihre nav - Controller zu bauen , wenn Sie es vorziehen ,

  3. Sie müssen die Variable wirklich nicht einmal irgendwo aufbewahren, da sie .navigationControllerimmer als Eigenschaft verfügbar ist - einfach.

Sobald Sie einen Navigationscontroller haben, ist es wirklich trivial, Übergänge zwischen Bildschirmen durchzuführen.

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

und du kannst pop.

Das gibt Ihnen jedoch nur den Standard-Apfel-Dual-Push-Effekt ...

(Der alte rutscht mit einer niedrigeren Geschwindigkeit ab, während der neue aufrutscht.)

Im Allgemeinen und überraschend müssen Sie sich normalerweise die Mühe machen, einen vollständigen benutzerdefinierten Übergang durchzuführen.

Selbst wenn Sie nur den einfachsten, häufigsten Übergang zum Verschieben / Verschieben wünschen, müssen Sie einen vollständigen benutzerdefinierten Übergang durchführen.

Glücklicherweise gibt es dazu einen Code zum Ausschneiden und Einfügen von Boilerplates in dieser Qualitätssicherung ... https://stackoverflow.com/a/48081504/294884 . Frohes neues Jahr 2018!

Fattie
quelle
3

Importieren Sie UIKit und erstellen Sie eine Erweiterung für 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)
}}

nach einfachem anruf:

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

von links nach rechts:

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

Sie können die Dauer mit Ihrer bevorzugten Dauer ändern ...

Fabio
quelle
1

Versuche dies.

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
quelle
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)
Chili
quelle
Ich habe dies in meinem Ereignis mit Schaltflächenklick für die Animation hinzugefügt. Es funktioniert für mich in Xcode 10.2 Swift 4.
Chilli
0

Präsentieren Sie den View Controller in iOS mit Swift von rechts nach links

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

Aktualisieren von: [ https://developer.apple.com/documentation/quartzcore/catransition weibl. [1 ]

FURKAN VIJAPURA
quelle
-1

Ich habe eine bessere Lösung, die für mich funktioniert hat, wenn Sie die klassische Animation von Apple beibehalten möchten, ohne den "schwarzen" Übergang zu sehen, den Sie mit CATransition () sehen. Verwenden Sie einfach die popToViewController-Methode.

Sie können nach dem ViewController suchen, den Sie benötigen

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

Sie können nach dem ViewController suchen, den Sie benötigen, indem Sie nach dessen Wiederherstellungs-ID suchen.

ACHTUNG: Wenn Sie beispielsweise durch 3 Ansichts-Controller navigieren und zum letzten gelangen, möchten Sie den ersten Popup-Vorgang ausführen. In diesem Fall verlieren Sie den Verweis auf den effektiven SECOND View Controller, da der popToViewController ihn überschrieben hat. Übrigens gibt es eine Lösung: Sie können die VC, die Sie später benötigen, problemlos vor dem popToViewcontroller speichern. Es hat bei mir sehr gut funktioniert.

Edoardo Vicoli
quelle
-4

Das hat bei mir funktioniert:

self.navigationController?.pushViewController(controller, animated: true)
Mathis Delaunay
quelle
Ich wollte es nicht in den Navigationsstapel schieben.
Umair Afzal