【发布时间】:2019-05-19 10:52:56
【问题描述】:
使用 Xcode-10.2.1、iOS-12.3、Swift-5.0.1,
我在使用自定义 UIViewControllerAnimatedTransitioning 动画时遇到了一个未知背景视图的奇怪问题!
问题在于,每当自定义转换发生时,都会有一个不应该存在的彩色剩余视图!
在爆炸视图中,显然有一个未知的黄色视图可见。问题是为什么它首先存在,它来自哪里? (我认为是 CircularCustomTranstion 导致了问题 - 继续阅读......)
这里有两个自定义过渡视频,说明了未知黄色视图的问题。 (对应的代码可以在下面找到……):
背景应该是全黑的,但是有一个奇怪的未知黄色视图困扰着动画......
进一步调查发现,只有在我之前使用 CircularCustomTransition 时才会出现损坏的黄色视图(参见下面的代码 - originally taken from here)。
我发现,即使使用此 CircularCustomTransition 的 ViewController 被长时间关闭(并且内存不足),任何即将到来的转换仍然被破坏。 (即,运行 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!")
}
}
【问题讨论】:
标签: animation uiviewcontroller uikit transition navigationcontroller