Як представити контролер перегляду справа наліво в iOS за допомогою Swift


83

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

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

Це представляє новий екран знизу вгору, але я хочу, щоб він був представлений справа наліво без використання UINavigationController.

Я використовую Xib замість розкадрування, так як я можу це зробити?


коротше, повна відповідь: stackoverflow.com/a/48081504/294884
Fattie

Відповіді:


160

Не має значення, чи є, xibчи storyboardвикористовуєте ви. Зазвичай перехід справа наліво використовується, коли ви натискаєте контролер перегляду на презентатор UINavigiationController.

ОНОВЛЕННЯ

Додана функція синхронізації 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)


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


Свіфт 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.


2
Я спробував використати код Swift3 в методі perform () користувацького UIStoryboardSegue, проблема полягає в тому, що перехід здійснюється в тому ж поданні, а потім з'являється другий вигляд. Будь-які ідеї щодо цього?
Деварші

2
uНаразі, коли я використовую цей метод, джерело VC згасає. Як я можу видалити цей ефект затухання і мати його так само, як анімація push?
Umair Afzal

1
@UmairAfzal Це колір вікна, вам потрібно змінити колір вікна, щоб уникнути цього.
Абдул Рехман,

Як ефективно використовувати цей перехід за допомогою UIModalPresentationStyle.overCurrentContext у Swift 3
Mamta

3
Якщо ви перетворите "анімоване" на "справжнє", воно також матиме невелику анімацію вгору. Я рекомендую встановити його на "false"
Rebeloper

68

Повний код для присутності / відмови, 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)
    }
}

2
Будь ласка, будьте більш конкретні. Поясніть більше!
Емм

3
Проголосуючи за вашу відповідь, оскільки вона також має функцію звільнення
Rebeloper

1
Один з найкращих там !! 👌
Шям

44

Прочитайте всі відповіді і не бачите правильного рішення. Правильний спосіб зробити це - зробити власний 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)


Молодці! Я деякий час випробовував усі відповіді, і там щось не так ... Я навіть підтримав того, який не повинен був бути ...
Реймонд Хілл,

коли ти відхиляєш представлений ВК, ти просто викликаєш відхилити (анімоване: істина, завершення: нуль) ???
Реймонд Хілл,

@ReimondHill так, я просто використовую звичайний DismissViewController
HotJard

Це, безумовно, найкраще рішення. Так, це трохи складніше, але він міцний і не зламається в різних умовах. Прийнята відповідь - хак.
mmh02,

Він чудово працює на всіх пристроях, крім iPhone 7+, 8+, X, XMax. Будь-яка ідея чому? vc не приймає повного кадру.
Умайр Афзал

14

Ви також можете скористатися користувацьким segue.

Стрімкий 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)
        })
    }
}

Краща відповідь, ніж інша, якщо вона використовується в презентації в поточному контексті
Варун Нахарія,

Відхиляючи цей звичайний сегмент, він все ще за замовчуванням відповідає оригінальному типу сегментації. Чи можна це обійти?
Alex Bailey

1
в той час як власні серії чудові - вони є лише рішенням розкадровки
Fattie,

7

Спробуйте це,

    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у вашому випадку.


3
Дякую, але наступний ВК з'являється раптово, замість того, щоб з'являтися справа наліво. Я змінив тривалість анімації, але все одно
Umair Afzal

4

Якщо ви хочете скористатися методом "швидкого виправлення" 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 і натисніть "вбудувати -> в контролер навігації". Дійсно, все. Або,

  2. в didFinishLaunchingWithOptionsньому легко побудувати свій контролер навігації, якщо вам подобається

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

Дійсно, коли у вас є NaviController, переходити між екранами стає тривіально,

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

і ви можете pop .

Однак це дає лише стандартний ефект "подвійного натискання" на яблука ...

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

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

Навіть якщо ви просто хочете найпростіший, найпоширеніший перехід / переміщення, вам доведеться виконати повний перехід на замовлення.

На щастя, для цього в цьому QA є якийсь вирізаний та вставлений шаблонний код ... https://stackoverflow.com/a/48081504/294884 . З новим 2018 роком!


3

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

Ви можете змінити тривалість на бажану тривалість ...


1

Спробуйте це.

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)

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)

Я додав це у свою подію, натиснуту кнопку для анімації. Для мене це працює в Xcode 10.2 swift 4.
Chilli

0

представити контролер подання справа наліво в 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

У мене є краще рішення, яке підійшло мені, якщо ви хочете зберегти класичну анімацію Apple, не бачачи того "чорного" переходу, який ви бачите за допомогою CATransition (). Просто використовуйте метод popToViewController.

Ви можете шукати потрібний вам viewController

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

Ви можете шукати viewController, який вам потрібен, шукаючи його restoreIdentifier.

БУДЬТЕ ОБЕРЕЖНІ: наприклад, якщо ви переходите через 3 контролери перегляду, а коли прийдете до останнього, ви хочете створити перший. У цьому випадку ви втратите посилання на діючий ДРУГИЙ контролер подання, оскільки popToViewController його замінив. До речі, є рішення: ви можете легко зберегти перед контролером popToView контролер, який вам знадобиться пізніше. У мене це спрацювало дуже добре.


Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.