Использование Xcode-10.2.1, iOS-12.3, Swift-5.0.1,
У меня странная проблема с неизвестным фоновым изображением при использовании пользовательских UIViewControllerAnimatedTransitioning
анимаций!
Проблема в том, что всякий раз, когда происходит пользовательский переход, появляется цветное левое представление, которого там не должно быть!
Во взрывном изображении явно виден неизвестный желтый вид.Вопрос в том, почему он вообще существует и откуда он?(Я полагал, что CircularCustomTranstion вызывает проблему - продолжайте читать ...)
Вот два видео с пользовательским переходом, которые иллюстрируют проблемус неизвестным желтым видом.(соответствующий код можно найти ниже ...):
Фоны должны быть полностью черными, но есть странный неизвестный желтый вид, мешающий анимации ...
Дальнейшие исследования приводят к выводу, что искаженное представление желтого цвета происходит только в том случае, если я ранее использовал CircularCustomTransition (см. Код ниже- изначально взято отсюда ).
Я обнаружил, что даже если контроллеры ViewController, использующие этот CircularCustomTransition, длительно отклоняются (и не хватает памяти), все предстоящие переходы все еще повреждены.(т.е. достаточно запустить CircularCustomTransition, и все последующие переходы покажут этот неприятный нежелательный фоновый вид)
Мой вопрос: почему использование приведенного ниже CircularCustomTransition повреждает все последующие фоны CustomTransition (в видео) ??
Может кто-нибудь помочь мне найти решение о том, как соответствующим образом изменить код CircularCustomTransition?
Вот видео, показывающее, как работает CircularCostomTransition (то есть работает, нок сожалению, любой последующий переход будет поврежден желтым представлением):
Вот код для CircularCustomTransition (ЧТО НЕПРАВИЛЬНО С ЭТИМ?)
import UIKit
protocol CircleTransitionable {
var triggerButton: UIButton { get }
var contentTextView: UITextView { get }
var mainView: UIView { get }
}
class CircularTransition: CustomAnimator {
weak var context: UIViewControllerContextTransitioning?
public override init(duration: TimeInterval = 0.25) {
super.init(duration: duration)
}
// make this zero for now and see if it matters when it comes time to make it interactive
override func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return 0.0
}
override func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
guard let fromVC = transitionContext.viewController(forKey: .from) as? CircleTransitionable,
let toVC = transitionContext.viewController(forKey: .to) as? CircleTransitionable,
let snapshot = fromVC.mainView.snapshotView(afterScreenUpdates: false) else {
transitionContext.completeTransition(false)
return
}
context = transitionContext
let containerView = transitionContext.containerView
// Background View With Correct Color
let backgroundView = UIView()
backgroundView.frame = toVC.mainView.frame
backgroundView.backgroundColor = fromVC.mainView.backgroundColor
containerView.addSubview(backgroundView)
// Animate old view offscreen
containerView.addSubview(snapshot)
fromVC.mainView.removeFromSuperview()
animateOldTextOffscreen(fromView: snapshot)
// Growing Circular Mask
containerView.addSubview(toVC.mainView)
animate(toView: toVC.mainView, fromTriggerButton: fromVC.triggerButton)
// Animate Text in with a Fade
animateToTextView(toTextView: toVC.contentTextView, fromTriggerButton: fromVC.triggerButton)
}
func animateOldTextOffscreen(fromView: UIView) {
UIView.animate(withDuration: 0.25, delay: 0.0, options: [.curveEaseIn], animations: {
fromView.center = CGPoint(x: fromView.center.x - 1000, y: fromView.center.y + 1500)
fromView.transform = CGAffineTransform(scaleX: 5.0, y: 5.0)
}, completion: { (finished) in
self.context?.completeTransition(finished)
})
}
func animate(toView: UIView, fromTriggerButton triggerButton: UIButton) {
// Starting Path
let rect = CGRect(x: triggerButton.frame.origin.x,
y: triggerButton.frame.origin.y,
width: triggerButton.frame.width,
height: triggerButton.frame.width)
let circleMaskPathInitial = UIBezierPath(ovalIn: rect)
// Destination Path
let fullHeight = toView.bounds.height
let extremePoint = CGPoint(x: triggerButton.center.x,
y: fullHeight)
let radius = sqrt((extremePoint.x*extremePoint.x) +
(extremePoint.y*extremePoint.y))
let circleMaskPathFinal = UIBezierPath(ovalIn: triggerButton.frame.insetBy(dx: -radius,
dy: -radius))
// Actual mask layer
let maskLayer = CAShapeLayer()
maskLayer.path = circleMaskPathFinal.cgPath
toView.layer.mask = maskLayer
// Mask Animation
let maskLayerAnimation = CABasicAnimation(keyPath: "path")
maskLayerAnimation.fromValue = circleMaskPathInitial.cgPath
maskLayerAnimation.toValue = circleMaskPathFinal.cgPath
maskLayerAnimation.delegate = self
maskLayer.add(maskLayerAnimation, forKey: "path")
context?.completeTransition(true)
}
func animateToTextView(toTextView: UIView, fromTriggerButton: UIButton) {
// Start toView offscreen a little and animate it to normal
let originalCenter = toTextView.center
toTextView.alpha = 0.0
toTextView.center = fromTriggerButton.center
toTextView.transform = CGAffineTransform(scaleX: 0.1, y: 0.1)
UIView.animate(withDuration: 0.25, delay: 0.1, options: [.curveEaseOut], animations: {
toTextView.transform = CGAffineTransform(scaleX: 1.0, y: 1.0)
toTextView.center = originalCenter
toTextView.alpha = 1.0
}, completion: { (finished) in
self.context?.completeTransition(finished)
})
}
}
extension CircularTransition: CAAnimationDelegate {
func animationDidStop(_ anim: CAAnimation, finished flag: Bool) {
context?.completeTransition(true)
}
}
По причинам полноты, вот код для двух анимаций, показанных в видео выше ...
class CustomBounceUpAnimationController: CustomAnimator {
override func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return 2.5
}
override func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
let fromViewController = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from)!
let toViewController = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to)!
let finalFrameForVC = transitionContext.finalFrame(for: toViewController)
let containerView = transitionContext.containerView
containerView.bringSubviewToFront(toViewController.view)
let bounds = UIScreen.main.bounds
toViewController.view.frame = finalFrameForVC.offsetBy(dx: 0.0, dy: bounds.size.height)
// toViewController.view.frame = CGRectOffset(finalFrameForVC, 0, -bounds.size.height)
containerView.addSubview(toViewController.view)
UIView.animate(withDuration: transitionDuration(using: transitionContext), delay: 0.0, usingSpringWithDamping: 0.5, initialSpringVelocity: 0.0, options: .curveLinear, animations: {
fromViewController.view.alpha = 0.5
toViewController.view.frame = finalFrameForVC
}) {
finished in
transitionContext.completeTransition(true)
fromViewController.view.alpha = 1.0
}
UIView.animate(withDuration: transitionDuration(using: transitionContext), delay: 0.0, usingSpringWithDamping: 0.5, initialSpringVelocity: 0.0, options: .curveLinear, animations: {
fromViewController.view.alpha = 0.5
toViewController.view.frame = finalFrameForVC
}, completion: {
finished in
transitionContext.completeTransition(true)
fromViewController.view.alpha = 1.0
})
}
}
class Custom3DAnimationController: CustomAnimator {
var reverse: Bool = false
override func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return 1.5
}
override func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
let containerView = transitionContext.containerView
let toViewController = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to)!
let fromViewController = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from)!
if let toView = toViewController.view {
containerView.subviews[0].backgroundColor = .clear
containerView.bringSubviewToFront(toView)
let fromView = fromViewController.view
let direction: CGFloat = reverse ? -1 : 1
let const: CGFloat = -0.005
toView.layer.anchorPoint = CGPoint(x: direction == 1 ? 0 : 1, y: 0.5)
fromView?.layer.anchorPoint = CGPoint(x: direction == 1 ? 1 : 0, y: 0.5)
var viewFromTransform: CATransform3D = CATransform3DMakeRotation(direction * CGFloat(Double.pi/2), 0.0, 1.0, 0.0)
var viewToTransform: CATransform3D = CATransform3DMakeRotation(-direction * CGFloat(Double.pi/2), 0.0, 1.0, 0.0)
viewFromTransform.m34 = const
viewToTransform.m34 = const
containerView.transform = CGAffineTransform(translationX: direction * containerView.frame.size.width / 2.0, y: 0)
toView.layer.transform = viewToTransform
containerView.addSubview(toView)
UIView.animate(withDuration: transitionDuration(using: transitionContext), animations: {
containerView.transform = CGAffineTransform(translationX: -direction * containerView.frame.size.width / 2.0, y: 0)
fromView?.layer.transform = viewFromTransform
toView.layer.transform = CATransform3DIdentity
}, completion: {
finished in
containerView.transform = .identity
fromView?.layer.transform = CATransform3DIdentity
toView.layer.transform = CATransform3DIdentity
fromView?.layer.anchorPoint = CGPoint(x: 0.5, y: 0.5)
toView.layer.anchorPoint = CGPoint(x: 0.5, y: 0.5)
if (transitionContext.transitionWasCancelled) {
toView.removeFromSuperview()
} else {
fromView?.removeFromSuperview()
}
transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
})
}
}
}
import UIKit
open class CustomAnimator: NSObject, UIViewControllerAnimatedTransitioning {
public enum TransitionType {
case navigation
case modal
}
let duration: TimeInterval
public init(duration: TimeInterval = 0.25) {
self.duration = duration
super.init()
}
open func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return self.duration
}
open func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
fatalError("You have to implement this method for yourself!")
}
}