iOS 13, кажется, использует новый UIPresentationController
для представления контроллеров модального представления, но тот, который не полагается на создание моментальных снимков контроллера представления представления (как это делают большинство / все библиотеки). Контроллер представления представления является «живым» и продолжает отображать анимацию/изменения, в то время как контроллер модального представления отображается поверх прозрачного/тонированного фона.
Я могу легко воспроизвести это (поскольку цель состоит в том, чтобы сделать обратно совместимую версию для iOS 10/11/12 и т. д.), используя CGAffineTransform
в представлении контроллера представления, однако часто при вращении устройства представление представления начинается деформироваться и расти за пределы, по-видимому, потому, что система обновляет свой frame
, пока к нему применяется активный transform
.
Согласно документации, frame
не определено, когда к представлению применяется transform
. Учитывая, что система, похоже, изменяет фрейм, а не я, как мне решить эту проблему, не прибегая к хакерским решениям, в которых я обновляю границы представления представления? Мне нужно, чтобы этот контроллер презентации оставался универсальным, поскольку контроллер представления может быть любой формы и формы и не обязательно будет полноэкранным представлением.
Вот что у меня есть до сих пор - это простой подкласс UIPresentationController
, который, кажется, работает нормально, однако вращение устройства, а затем отклонение представленного контроллера представления, кажется, деформирует границы представления контроллера представления (становится слишком широким или сжимается, в зависимости от того, вы представили модальный контроллер в альбомной/портретной ориентации)
class SheetPresentationController: UIPresentationController {
override var frameOfPresentedViewInContainerView: CGRect {
return CGRect(x: 40, y: containerView!.bounds.height / 2, width: containerView!.bounds.width-80, height: containerView!.bounds.height / 2)
}
override func containerViewWillLayoutSubviews() {
super.containerViewWillLayoutSubviews()
if let _ = presentingViewController.transitionCoordinator {
// We're transitioning - don't touch the frame yet as it'll
// clash with our transform
} else {
self.presentedView?.frame = self.frameOfPresentedViewInContainerView
}
}
override func presentationTransitionWillBegin() {
super.presentationTransitionWillBegin()
containerView?.backgroundColor = .clear
if let coordinator = presentingViewController.transitionCoordinator {
coordinator.animate(alongsideTransition: { [weak self] _ in
self?.containerView?.backgroundColor = UIColor.black.withAlphaComponent(0.3)
// Scale the presenting view
self?.presentingViewController.view.layer.cornerRadius = 16
self?.presentingViewController.view.transform = CGAffineTransform.init(scaleX: 0.9, y: 0.9)
}, completion: nil)
}
}
override func dismissalTransitionWillBegin() {
if let coordinator = presentingViewController.transitionCoordinator {
coordinator.animate(alongsideTransition: { [weak self] _ in
self?.containerView?.backgroundColor = .clear
self?.presentingViewController.view.layer.cornerRadius = 0
self?.presentingViewController.view.transform = .identity
}, completion: nil)
}
}
}
И контроллер Presenting Animation:
import UIKit
final class PresentingAnimationController: NSObject, UIViewControllerAnimatedTransitioning {
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
guard let presentedViewController = transitionContext.viewController(forKey: .to) else {
return
}
let springTiming = UISpringTimingParameters(dampingRatio: 1.0, initialVelocity: CGVector(dx:1.0, dy: 1.0))
let animator: UIViewPropertyAnimator = UIViewPropertyAnimator(duration: transitionDuration(using: transitionContext), timingParameters: springTiming)
let containerView = transitionContext.containerView
containerView.addSubview(presentedViewController.view)
let finalFrameForPresentedView = transitionContext.finalFrame(for: presentedViewController)
presentedViewController.view.frame = finalFrameForPresentedView
// Move it below the screen so it slides up
presentedViewController.view.frame.origin.y = containerView.bounds.height
animator.addAnimations {
presentedViewController.view.frame = finalFrameForPresentedView
}
animator.addCompletion { (animationPosition) in
if animationPosition == .end {
transitionContext.completeTransition(true)
}
}
animator.startAnimation()
}
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return 0.6
}
}
А также отклоняющий контроллер анимации:
import UIKit
final class DismissingAnimationController: NSObject, UIViewControllerAnimatedTransitioning {
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
guard let presentedViewController = transitionContext.viewController(forKey: .from) else {
return
}
guard let presentingViewController = transitionContext.viewController(forKey: .to) else {
return
}
let finalFrameForPresentedView = transitionContext.finalFrame(for: presentedViewController)
let containerView = transitionContext.containerView
let offscreenFrame = CGRect(x: finalFrameForPresentedView.minX, y: containerView.bounds.height, width: finalFrameForPresentedView.width, height: finalFrameForPresentedView.height)
let springTiming = UISpringTimingParameters(dampingRatio: 1.0, initialVelocity: CGVector(dx:1.0, dy: 1.0))
let animator: UIViewPropertyAnimator = UIViewPropertyAnimator(duration: transitionDuration(using: transitionContext), timingParameters: springTiming)
animator.addAnimations {
presentedViewController.view.frame = offscreenFrame
}
animator.addCompletion { (position) in
if position == .end {
// Complete transition
transitionContext.completeTransition(true)
}
}
animator.startAnimation()
}
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return 0.6
}
}