Как анимировать вкладку Tab tab с переходом слайда CrossDissolve?
Я пытаюсь создать эффект перехода на UITabBarController
чем-то похожем на приложение Facebook. Мне удалось получить "эффект прокрутки", работающий на переключателе табуляции, но я не могу понять, как перекрестное растворение (или оно по крайней мере не работает).
Вот мой текущий код:
import UIKit
class ScrollingTabBarControllerDelegate: NSObject, UITabBarControllerDelegate {
func tabBarController(_ tabBarController: UITabBarController, animationControllerForTransitionFrom fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return ScrollingTransitionAnimator(tabBarController: tabBarController, lastIndex: tabBarController.selectedIndex)
}
}
class ScrollingTransitionAnimator: NSObject, UIViewControllerAnimatedTransitioning {
weak var transitionContext: UIViewControllerContextTransitioning?
var tabBarController: UITabBarController!
var lastIndex = 0
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return 0.2
}
init(tabBarController: UITabBarController, lastIndex: Int) {
self.tabBarController = tabBarController
self.lastIndex = lastIndex
}
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
self.transitionContext = transitionContext
let containerView = transitionContext.containerView
let fromViewController = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from)
let toViewController = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to)
containerView.addSubview(toViewController!.view)
var viewWidth = toViewController!.view.bounds.width
if tabBarController.selectedIndex < lastIndex {
viewWidth = -viewWidth
}
toViewController!.view.transform = CGAffineTransform(translationX: viewWidth, y: 0)
UIView.animate(withDuration: self.transitionDuration(using: (self.transitionContext)), delay: 0.0, usingSpringWithDamping: 1.2, initialSpringVelocity: 2.5, options: .transitionCrossDissolve, animations: {
toViewController!.view.transform = CGAffineTransform.identity
fromViewController!.view.transform = CGAffineTransform(translationX: -viewWidth, y: 0)
}, completion: { _ in
self.transitionContext?.completeTransition(!self.transitionContext!.transitionWasCancelled)
fromViewController!.view.transform = CGAffineTransform.identity
})
}
}
Было бы замечательно, если бы кто-нибудь знал, как заставить это работать, пытаясь в течение многих дней без прогресса...:/
редактировать: я получил работу перекрестного растворения, заменив блок UIView.animate на:
UIView.transition(with: containerView, duration: 0.2, options: .transitionCrossDissolve, animations: {
toViewController!.view.transform = CGAffineTransform.identity
fromViewController!.view.transform = CGAffineTransform(translationX: -viewWidth, y: 0)
}, completion: { _ in
self.transitionContext?.completeTransition(!self.transitionContext!.transitionWasCancelled)
fromViewController!.view.transform = CGAffineTransform.identity
})
Тем не менее, анимация действительно запаздывает и не используется :(
edit 2: Для людей, пытающихся использовать эти фрагменты, не забудьте подключить делегат для UITabBarController
, иначе ничего не произойдет.
редактировать 3: я нашел библиотеку Swift, которая делает именно то, что я искал: https://github.com/Interactive-Studio/TransitionableTab
Ответы
Ответ 1
Есть более простой способ сделать это. Добавьте следующий код в делегат панели вкладок:
Работаем на Swift 2, 3 и 4
class MySubclassedTabBarController: UITabBarController {
override func viewDidLoad() {
super.viewDidLoad()
delegate = self
}
}
extension MySubclassedTabBarController: UITabBarControllerDelegate {
func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool {
guard let fromView = selectedViewController?.view, let toView = viewController.view else {
return false // Make sure you want this as false
}
if fromView != toView {
UIView.transition(from: fromView, to: toView, duration: 0.3, options: [.transitionCrossDissolve], completion: nil)
}
return true
}
}
РЕДАКТИРОВАТЬ (23/23/18) Так как этот ответ становится популярным, я обновил код, чтобы убрать принудительное развертывание, что является плохой практикой, и добавил защитное утверждение.
РЕДАКТИРОВАТЬ (7/11/18) @AlbertoGarcía прав. Если дважды коснуться значка панели вкладок, вы увидите пустой экран. Поэтому я добавил дополнительную проверку
Ответ 2
Если вы хотите использовать UIViewControllerAnimatedTransitioning
чтобы сделать что-то более нестандартное, чем UIView.transition
, взгляните на эту суть.
![enter image description here]()
// MyTabController.swift
import UIKit
class MyTabBarController: UITabBarController {
override func viewDidLoad() {
super.viewDidLoad()
delegate = self
}
}
extension MyTabBarController: UITabBarControllerDelegate {
func tabBarController(_ tabBarController: UITabBarController, animationControllerForTransitionFrom fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return MyTransition(viewControllers: tabBarController.viewControllers)
}
}
class MyTransition: NSObject, UIViewControllerAnimatedTransitioning {
let viewControllers: [UIViewController]?
let transitionDuration: Double = 1
init(viewControllers: [UIViewController]?) {
self.viewControllers = viewControllers
}
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return TimeInterval(transitionDuration)
}
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
guard
let fromVC = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from),
let fromView = fromVC.view,
let fromIndex = getIndex(forViewController: fromVC),
let toVC = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to),
let toView = toVC.view,
let toIndex = getIndex(forViewController: toVC)
else {
transitionContext.completeTransition(false)
return
}
let frame = transitionContext.initialFrame(for: fromVC)
var fromFrameEnd = frame
var toFrameStart = frame
fromFrameEnd.origin.x = toIndex > fromIndex ? frame.origin.x - frame.width : frame.origin.x + frame.width
toFrameStart.origin.x = toIndex > fromIndex ? frame.origin.x + frame.width : frame.origin.x - frame.width
toView.frame = toFrameStart
DispatchQueue.main.async {
transitionContext.containerView.addSubview(toView)
UIView.animate(withDuration: self.transitionDuration, animations: {
fromView.frame = fromFrameEnd
toView.frame = frame
}, completion: {success in
fromView.removeFromSuperview()
transitionContext.completeTransition(success)
})
}
}
func getIndex(forViewController vc: UIViewController) -> Int? {
guard let vcs = self.viewControllers else { return nil }
for (index, thisVC) in vcs.enumerated() {
if thisVC == vc { return index }
}
return nil
}
}
Ответ 3
Чтобы развернуть ответ @gmogames: fooobar.com/info/1268510/...
Я не смог получить анимацию при выборе индекса панели вкладок с помощью кода, например:
tabBarController.setSeletedIndex(0)
Похоже, не проходит через тот же heirarchy вызова, и он пропускает метод:
tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController)
полностью, в результате чего нет анимации.
В моем коде я хотел сделать анимационный переход для пользователя, касающегося элемента панели вкладок, в дополнение к тому, что при определенных обстоятельствах я вручную настраивал код элемента панели вкладок.
Вот мое дополнение к решению выше, которое добавляет другой метод для установки выбранного индекса через код, который будет анимировать переход:
import Foundation
import UIKit
@objc class CustomTabBarController: UITabBarController {
override func viewDidLoad() {
super.viewDidLoad()
delegate = self
}
@objc func set(selectedIndex index : Int) {
_ = self.tabBarController(self, shouldSelect: self.viewControllers![index])
}
}
@objc extension CustomTabBarController: UITabBarControllerDelegate {
@objc func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool {
guard let fromView = selectedViewController?.view, let toView = viewController.view else {
return false // Make sure you want this as false
}
if fromView != toView {
UIView.transition(from: fromView, to: toView, duration: 0.3, options: [.transitionCrossDissolve], completion: { (true) in
})
self.selectedViewController = viewController
}
return true
}
}
Теперь просто позвоните
tabBarController.setSelectedWithIndex(1)
для анимационного перехода в коде!
Я все еще думаю, что к сожалению, чтобы сделать это, мы должны переопределить метод, который не является установщиком, и манипулировать данными внутри него. Это не делает контроллер панели вкладок таким расширяемым, каким он должен быть, если это метод, который нам нужно переопределить, чтобы сделать это.