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


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)

Похоже, что animatedпараметр в presentViewControllerметоде не имеет значения в этом случае пользовательского перехода. Это может быть любое значение, либо trueили false.


2
Я попытался использовать код Swift3 в методе perform () пользовательского UIStoryboardSegue, проблема в том, что переход выполняется в том же представлении, а затем появляется второе представление. Есть идеи по этому поводу?
Деварши

2
В настоящее время, когда я использую этот метод, исходный VC исчезает. Как я могу удалить этот эффект затухания и получить его точно так же, как анимацию push?
Умайр Афзал

1
@UmairAfzal Это цвет окна, вам нужно изменить цвет окна, чтобы этого избежать.
Абдул Рехман

Как эффективно использовать этот переход с UIModalPresentationStyle.overCurrentContext в Swift 3
Мамта

3
Если вы установите для параметра «Animated» значение «true», у него также будет небольшая восходящая анимация. Я рекомендую установить его на "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
Пожалуйста, будьте более конкретными. Объясни подробней !
Emm

3
Поддерживаю свой ответ, потому что в нем тоже есть функция отклонения
Ребелопер

1
Один из лучших !! 👌
Shyam

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)


Отлично сработано! Я пробовал все ответы какое-то время, и что-то было не так ... Я даже проголосовал за Тот, которого не должно было быть ...
Реймонд Хилл

когда вы отклоняете представленный ВК, вы просто вызываете отклонение (анимировано: правда, завершение: ноль) ???
Reimond Hill

@ReimondHill да, я просто использую обычный dismissViewController
HotJard

Это, безусловно, лучшее решение. Да, он немного сложнее, но он прочен и не сломается в различных условиях. Принятый ответ - взлом.
mmh02

Он отлично работает на всех устройствах, кроме iPhone 7+, 8+, X, XMax. Есть идеи, почему? видеомагнитофон не принимает полный кадр.
Umair Afzal

14

Вы также можете использовать пользовательский переход.

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

Лучший ответ, чем другой, если он используется в презентации в текущем контексте
Варун Нахария

При отклонении этого настраиваемого перехода по умолчанию используется исходный тип перехода. Есть ли способ обойти это?
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это ViewController, dashboardWorkoutв вашем случае.


3
Спасибо, но следующий VC появляется внезапно, а не справа налево. Я изменил длительность анимации, но остался прежним
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она всегда доступна как свойство - легко.

На самом деле, когда у вас есть navigationController, переходы между экранами тривиальны,

    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 visible [1 ]


-1

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

Вы можете найти нужный вам viewController в

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

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

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


Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.