#swift #xcode #uitabbarcontroller #uianimation
Вопрос:
У меня есть UITabBarController с 2 вкладками, между которыми пользователь может переключаться. Я пытаюсь анимировать переход между обеими вкладками. Вот результат, который я хочу получить:
Теперь это отлично работает на ряде устройств и симуляторов. Однако на некоторых устройствах я получаю именно это:
Таким образом, это не относится к версиям iOS, потому что он работает, например, на iPad Pro (12,9 дюйма) iOS 13.0 sim, но не на iPad Air 3-го поколения iOS 13.0.
Вот код для анимации:
class TabBarAnimationController: NSObject, UIViewControllerAnimatedTransitioning {
struct Constants {
static let duration = 1.0
static let initialDestinationAlpha: CGFloat = 0.0
static let finalDestinationAlpha: CGFloat = 1.0
static let buttonPadding: CGFloat = 30
static let dateIndex = 5
static let commanderIDIndex = 4
}
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
guard let destination = transitionContext.view(forKey: UITransitionContextViewKey.to),
let fromVC = transitionContext.viewController(forKey: .from)?.children.first as? BaseFlightLookupViewController,
let toVc = transitionContext.viewController(forKey: .to)?.children.first as? BaseFlightLookupViewController
else { return }
destination.alpha = Constants.initialDestinationAlpha
transitionContext.containerView.addSubview(destination)
// Setup constraints to be used for view setup prior to animation
var loginSetupConstraints = [NSLayoutConstraint]()
var retrieveSetupConstraints = [NSLayoutConstraint]()
// These constraints are activated during animation and removed once complete
var fromLoginConstraints = [NSLayoutConstraint]()
var fromRetrieveConstraints = [NSLayoutConstraint]()
// Pre-animation setup for transition from login to retrieve
if let from = fromVC as? AircraftLoginViewController, let to = toVc as? RetrieveCompletedOrderViewController {
loginSetupConstraints = [
to.date.trailingAnchor.constraint(equalTo: from.view.leadingAnchor),
to.date.leadingAnchor.constraint(equalTo: from.view.leadingAnchor, constant: -from.iata.frame.width),
to.date.bottomAnchor.constraint(equalTo: from.submitButton.topAnchor, constant: -Constants.buttonPadding)
]
to.date.setFieldColor(UIColor.i6.blue)
from.stackView.addSubview(to.date)
loginSetupConstraints.forEach { $0.isActive = true }
from.view.layoutIfNeeded()
}
// Pre-animation setup for transition from retrieve to login
if let from = fromVC as? RetrieveCompletedOrderViewController,
let to = toVc as? AircraftLoginViewController,
let useCommanderID = AppConfig.current.config?.shouldUseCommanderID,
useCommanderID {
retrieveSetupConstraints = [
to.commanderId.topAnchor.constraint(equalTo: from.aircraftRegistration.bottomAnchor),
to.commanderId.trailingAnchor.constraint(equalTo: from.view.leadingAnchor),
to.commanderId.leadingAnchor.constraint(equalTo: from.view.leadingAnchor, constant: -to.commanderId.frame.width)
]
to.commanderId.setFieldColor(UIColor.i6.purple)
from.stackView.addSubview(to.commanderId)
retrieveSetupConstraints.forEach { $0.isActive = true }
from.view.layoutIfNeeded()
}
UIView.animate(withDuration: transitionDuration(using: transitionContext), animations: {
if let from = fromVC as? AircraftLoginViewController,
let to = toVc as? RetrieveCompletedOrderViewController {
fromLoginConstraints = [
to.date.topAnchor.constraint(equalTo: from.iata.bottomAnchor),
to.date.leadingAnchor.constraint(equalTo: from.flightNumber.leadingAnchor),
from.submitButton.topAnchor.constraint(equalTo: to.date.bottomAnchor, constant: Constants.buttonPadding)
]
// If commanderID is present, add relevant constraints
if let useCommanderID = AppConfig.current.config?.shouldUseCommanderID, useCommanderID {
fromLoginConstraints = [
from.iata.topAnchor.constraint(equalTo: from.aircraftRegistration.bottomAnchor),
from.commanderId.trailingAnchor.constraint(equalTo: from.view.leadingAnchor),
from.commanderId.leadingAnchor.constraint(equalTo: from.view.leadingAnchor, constant: -from.commanderId.frame.width)
]
}
self.transitionToRetrieveOrders(from: from, to: to, constraints: fromLoginConstraints, setupConstraints: loginSetupConstraints)
} else if let from = fromVC as? RetrieveCompletedOrderViewController,
let to = toVc as? AircraftLoginViewController {
fromRetrieveConstraints = [
from.date.trailingAnchor.constraint(equalTo: from.view.leadingAnchor),
from.date.leadingAnchor.constraint(equalTo: from.view.leadingAnchor, constant: -from.date.frame.width),
from.submitButton.topAnchor.constraint(equalTo: from.iata.bottomAnchor, constant: Constants.buttonPadding)
]
if let useCommanderID = AppConfig.current.config?.shouldUseCommanderID, useCommanderID {
fromRetrieveConstraints = [
to.commanderId.topAnchor.constraint(equalTo: from.aircraftRegistration.bottomAnchor),
to.commanderId.leadingAnchor.constraint(equalTo: from.flightNumber.leadingAnchor),
from.iata.topAnchor.constraint(equalTo: to.commanderId.bottomAnchor)
]
} else {
fromRetrieveConstraints.append(from.iata.topAnchor.constraint(equalTo: from.aircraftRegistration.bottomAnchor))
}
self.transitionToLogin(from: from, to: to, constraints: fromRetrieveConstraints, setupConstraints: retrieveSetupConstraints)
}
}, completion: {
destination.alpha = Constants.finalDestinationAlpha
destination.transform = .identity
if let from = fromVC as? AircraftLoginViewController {
self.finaliseTransition(fromVC: from, toVC: toVc, constraints: fromLoginConstraints)
} else {
self.finaliseTransition(fromVC: fromVC, toVC: toVc, constraints: fromRetrieveConstraints)
}
transitionContext.completeTransition($0)
})
}
private func transitionToRetrieveOrders(from: AircraftLoginViewController, to: RetrieveCompletedOrderViewController, constraints: [NSLayoutConstraint], setupConstraints: [NSLayoutConstraint]) {
guard let useCommanderID = AppConfig.current.config?.shouldUseCommanderID else { return }
// De-activate setup constraints
setupConstraints.forEach { $0.isActive = false }
// Activate animation constraints
constraints.forEach { $0.isActive = true }
let fields = useCommanderID ? [from.flightNumber, from.aircraftRegistration, from.commanderId, from.iata, to.date] :
[from.flightNumber, from.aircraftRegistration, from.iata, to.date]
fields.forEach { $0.setFieldColor(UIColor.i6.purple) }
from.submitButton.setColor(color: UIColor.i6.purple)
from.view.layoutIfNeeded()
}
private func transitionToLogin(from: RetrieveCompletedOrderViewController, to: AircraftLoginViewController, constraints: [NSLayoutConstraint], setupConstraints: [NSLayoutConstraint]) {
guard let useCommanderID = AppConfig.current.config?.shouldUseCommanderID else { return }
// De-activate setup constraints
if useCommanderID {
setupConstraints.forEach { $0.isActive = false }
}
// Activate animation constraints
constraints.forEach { $0.isActive = true }
// Set field and button color to match new view controller
let fields = useCommanderID ? [from.flightNumber, from.aircraftRegistration, to.commanderId, from.iata, from.date] :
[from.flightNumber, from.aircraftRegistration, from.iata, from.date]
fields.forEach { $0.setFieldColor(UIColor.i6.blue) }
from.submitButton.setColor(color: UIColor.i6.blue)
from.view.layoutIfNeeded()
}
private func finaliseTransition(fromVC: BaseFlightLookupViewController, toVC: BaseFlightLookupViewController, constraints: [NSLayoutConstraint]) {
if let from = fromVC as? AircraftLoginViewController, let to = toVC as? RetrieveCompletedOrderViewController {
to.submitButton.topAnchor.constraint(equalTo: to.date.bottomAnchor, constant: Constants.buttonPadding).isActive = true
// Deactivate constraints on from VC ready to transition back
constraints.forEach { $0.isActive = false }
// Reset field and button colours to initial state
let fields = [from.flightNumber, from.aircraftRegistration, from.commanderId, from.iata]
fields.forEach { $0.setFieldColor(UIColor.i6.blue) }
from.submitButton.setColor(color: UIColor.i6.blue)
to.stackView.insert(arrangedSubview: to.date, atIndex: Constants.dateIndex)
} else if let from = fromVC as? RetrieveCompletedOrderViewController, let to = toVC as? AircraftLoginViewController {
// Deactivate constraints on from VC ready to transition back
constraints.forEach { $0.isActive = false }
// Reset field and button colours to initial state
let fields = [from.flightNumber, from.aircraftRegistration, from.iata, from.date]
fields.forEach { $0.setFieldColor(UIColor.i6.purple) }
from.submitButton.setColor(color: UIColor.i6.purple)
if let useCommanderID = AppConfig.current.config?.shouldUseCommanderID, useCommanderID {
to.stackView.insert(arrangedSubview: to.commanderId, atIndex: Constants.commanderIDIndex)
}
}
}
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return Constants.duration
}
}
I’m struggling to see why this would be producing such different results on different sims.