- PVSM.RU - https://www.pvsm.ru -
Жил был скромный вью-контроллер VCYellow. И не было у него ни картинки, ни текста, ни даже малюсенькой бизнес логики. Жил он обычной вью-контроллерской жизнью.
Его товарищ вью-контроллер VCMain иногда презентовал его миру:
class VCMain: UIViewController {
...
@IBAction func onBtnTapMeTapped(_ sender: Any) {
let vcYellow = self.storyboard!.instantiateViewController(withIdentifier: "VCYellow") as! VCYellow
self.present(vcYellow, animated: true, completion: nil)
}
А VCYellow в свою очередь скрывался при помощи единственной кнопки "X", которой он, кстати говоря, очень гордился:
class VCYellow: UIViewController {
...
@IBAction func onBtnCloseTapped(_ sender: Any) {
self.dismiss(animated: true, completion: nil)
}
И выглядело это не то чтобы плохо, но скучно и обыденно:
Но была у нашего героя мечта научиться показываться и скрываться по-красоте. Да так, чтобы можно было эту красоту менять потом по праздникам или просто в честь хорошего настроения.
Шли года… и так и осталась бы мечта мечтой, если бы не узнал VCYellow о магии под названием:
UIViewControllerTransitioningDelegate
А сила этой магии в том, что даёт она возможность подсунуть соответствующий аниматор для показа и для скрытия вью-контроллера. Как раз то, о чём мечтал наш контроллер.
Прочитал он в древних свитках [1] как использовать заклятие и начал готовиться.
Записал себе шпаргалку с самим заклинанием, чтобы не забыть:
extension VCYellow: UIViewControllerTransitioningDelegate {
func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return AnimatorPresent(startFrame: self.startFrame)
}
func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return AnimatorDismiss(endFrame: self.startFrame)
}
}
В ней он тщательно расписал, что для показа нужно использовать аниматор AnimatorPresent, а при закрытии AnimatorDismiss.
Ну и в качестве помощи обоим аниматорам было решено передать фрейм главной кнопки из VCMain
А потом и сам морально настроился. Потому как без правильного настроя, как известно, никакая магия не работает:
override func viewDidLoad() {
super.viewDidLoad()
self.modalPresentationStyle = .custom
self.transitioningDelegate = self
}
Попросил он своего друга VCMain презентануть себя, чтобы проверить как магия сработает и… сработала она никак…
Оказалось, что AnimatorPresent и AnimatorDismiss сами собой не появляются.
Останавливаться было уже поздно и наш герой решил создать необходимые аниматоры. Поковырялся в нужном разделе древних свитков [2] и узнал, что для создания аниматора достаточно двух вещей.
Во-первых надо задать время, отведённое для анимации:
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return 0.3
}
а во-вторых обозначить саму анимацию:
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
//1
guard let vcTo = transitionContext.viewController(forKey: .to),
let snapshot = vcTo.view.snapshotView(afterScreenUpdates: true) else {
return
}
//2
let vContainer = transitionContext.containerView
//3
vcTo.view.isHidden = true
vContainer.addSubview(vcTo.view)
//4
snapshot.frame = self.startFrame
vContainer.addSubview(snapshot)
UIView.animate(withDuration: 0.3, animations: {
//5
snapshot.frame = (transitionContext.finalFrame(for: vcTo))
}, completion: { success in
//6
vcTo.view.isHidden = false
snapshot.removeFromSuperview()
transitionContext.completeTransition(true)
})
}
В результате вышел вот такой аниматор для показа:
import UIKit
class AnimatorPresent: NSObject, UIViewControllerAnimatedTransitioning {
let startFrame: CGRect
init(startFrame: CGRect) {
self.startFrame = startFrame
}
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return 0.3
}
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
guard let vcTo = transitionContext.viewController(forKey: .to),
let snapshot = vcTo.view.snapshotView(afterScreenUpdates: true) else {
return
}
let vContainer = transitionContext.containerView
vcTo.view.isHidden = true
vContainer.addSubview(vcTo.view)
snapshot.frame = self.startFrame
vContainer.addSubview(snapshot)
UIView.animate(withDuration: 0.3, animations: {
snapshot.frame = (transitionContext.finalFrame(for: vcTo))
}, completion: { success in
vcTo.view.isHidden = false
snapshot.removeFromSuperview()
transitionContext.completeTransition(true)
})
}
}
А после этого несложно было написать аниматор для скрывания, который делает примерно то же самое, но наоборот:
import UIKit
class AnimatorDismiss: NSObject, UIViewControllerAnimatedTransitioning {
let endFrame: CGRect
init(endFrame: CGRect) {
self.endFrame = endFrame
}
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return 0.3
}
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
guard let vcTo = transitionContext.viewController(forKey: .to),
let vcFrom = transitionContext.viewController(forKey: .from),
let snapshot = vcFrom.view.snapshotView(afterScreenUpdates: true) else {
return
}
let vContainer = transitionContext.containerView
vContainer.addSubview(vcTo.view)
vContainer.addSubview(snapshot)
vcFrom.view.isHidden = true
UIView.animate(withDuration: 0.3, animations: {
snapshot.frame = self.endFrame
}, completion: { success in
transitionContext.completeTransition(true)
})
}
}
Закончив все доделки, VCYellow опять попросил своего друга VCMain презентовать себя и о чудо!
Магия сработала! Мечта VCYellow сбылась! Теперь он может показываться и скрываться как ему захочется и ничто не будет ограничивать его фантазию!
Проект-пример можно скачать тут [3]
Статья, которую я использовал для вдохновения находится тут [4]
Автор: Кирилл
Источник [5]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/razrabotka-pod-ios/294359
Ссылки в тексте:
[1] древних свитках: https://developer.apple.com/documentation/uikit/uiviewcontrollertransitioningdelegate
[2] древних свитков: https://developer.apple.com/documentation/uikit/uiviewcontrolleranimatedtransitioning
[3] тут: https://github.com/funkydevil/customTransition
[4] тут: https://www.raywenderlich.com/322-custom-uiviewcontroller-transitions-getting-started
[5] Источник: https://habr.com/post/424853/?utm_source=habrahabr&utm_medium=rss&utm_campaign=424853
Нажмите здесь для печати.