Так как это лучший результат в Google, я подумал, что поделюсь тем, что я считаю самым разумным способом; который должен использовать API перехода iOS 7+. Я реализовал это для iOS 10 с Swift 3.
Довольно просто объединить это с тем, как UINavigationController
анимирует между двумя контроллерами представления, если вы создаете подкласс UINavigationController
и возвращаете экземпляр класса, который соответствует протоколу UIViewControllerAnimatedTransitioning
.
Например, вот мой UINavigationController
подкласс:
class NavigationController: UINavigationController {
init() {
super.init(nibName: nil, bundle: nil)
delegate = self
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
extension NavigationController: UINavigationControllerDelegate {
public func navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationControllerOperation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return NavigationControllerAnimation(operation: operation)
}
}
Вы можете видеть, что я установил UINavigationControllerDelegate
для себя, и в расширении моего подкласса я реализую метод в UINavigationControllerDelegate
, который позволяет вам возвращать пользовательский контроллер анимации (т.е. NavigationControllerAnimation
). Этот пользовательский контроллер анимации заменит вам стандартную анимацию.
Возможно, вам интересно, почему я передаю эту операцию экземпляру NavigationControllerAnimation
через его инициализатор. Я делаю это так, чтобы в реализации NavigationControllerAnimation
протокола UIViewControllerAnimatedTransitioning
я знал, что это за операция (то есть «push» или «pop»). Это помогает узнать, какую анимацию я должен делать. В большинстве случаев вы хотите выполнить другую анимацию в зависимости от операции.
Остальное довольно стандартно. Реализуйте две обязательные функции в протоколе UIViewControllerAnimatedTransitioning
и анимируйте, как вам нравится:
class NavigationControllerAnimation: NSObject, UIViewControllerAnimatedTransitioning {
let operation: UINavigationControllerOperation
init(operation: UINavigationControllerOperation) {
self.operation = operation
super.init()
}
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return 0.3
}
public func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
guard let fromViewController = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from),
let toViewController = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to) else { return }
let containerView = transitionContext.containerView
if operation == .push {
// do your animation for push
} else if operation == .pop {
// do your animation for pop
}
}
}
Важно помнить, что для каждого отдельного типа операции (т. Е. «Push» или «pop») контроллеры представления «в» и «из» будут разными. Когда вы работаете в режиме push, контроллером для просмотра будет тот, который выдвигается. Когда вы работаете в режиме pop, контроллер для просмотра будет тем, к которому осуществляется переход, и контроллером from для просмотра будет тот, который выдается.
Кроме того, контроллер вида to
должен быть добавлен как подпредставление containerView
в контексте перехода.
Когда ваша анимация завершится, вы должны позвонить transitionContext.completeTransition(true)
. Если вы делаете интерактивный переход, вам придется динамически возвращать Bool
к completeTransition(didComplete: Bool)
, в зависимости от того, завершен ли переход в конце анимации.
Наконец ( необязательное чтение ), вы можете посмотреть, как я выполнял переход, над которым я работал. Этот код немного более хакерский, и я написал его довольно быстро, поэтому я бы не сказал, что это отличный анимационный код, но он все же показывает, как выполнять анимационную часть.
Мой был действительно простой переход; Я хотел имитировать ту же анимацию, что обычно делает UINavigationController, но вместо анимации «следующая страница поверх», которую я это делал, я хотел реализовать анимацию 1: 1 старого контроллера представления в то же время, что и новое представление контроллер появляется. Это приводит к тому, что два контроллера представления выглядят так, как будто они прикреплены друг к другу.
Для операции выталкивания, которая сначала требует установки начала координат вида toViewController
на экране выключения оси x, добавление его в качестве подпредставления containerView
, анимация его на экране, установив для origin.x
значение нуль. В то же время я анимирую вид fromViewController
, установив origin.x
на экране:
toViewController.view.frame = containerView.bounds.offsetBy(dx: containerView.frame.size.width, dy: 0.0)
containerView.addSubview(toViewController.view)
UIView.animate(withDuration: transitionDuration(using: transitionContext),
delay: 0,
options: [ UIViewAnimationOptions.curveEaseOut ],
animations: {
toViewController.view.frame = containerView.bounds
fromViewController.view.frame = containerView.bounds.offsetBy(dx: -containerView.frame.size.width, dy: 0)
},
completion: { (finished) in
transitionContext.completeTransition(true)
})
Поп-операция в основном обратная. Добавьте toViewController
как подпредставление containerView
и анимируйте fromViewController
вправо, как вы анимируете в toViewController
слева:
containerView.addSubview(toViewController.view)
UIView.animate(withDuration: transitionDuration(using: transitionContext),
delay: 0,
options: [ UIViewAnimationOptions.curveEaseOut ],
animations: {
fromViewController.view.frame = containerView.bounds.offsetBy(dx: containerView.frame.width, dy: 0)
toViewController.view.frame = containerView.bounds
},
completion: { (finished) in
transitionContext.completeTransition(true)
})
Вот суть всего файла swift:
https://gist.github.com/alanzeino/603293f9da5cd0b7f6b60dc20bc766be