Ios 将自定义可设置动画的UIView属性与其他动画同步
我有一个Ios 将自定义可设置动画的UIView属性与其他动画同步,ios,swift,core-animation,calayer,uiviewanimation,Ios,Swift,Core Animation,Calayer,Uiviewanimation,我有一个UIView子类,它有一个自定义的可设置动画的属性,与其他视图上的其他(内置)UIView属性一起设置动画。我遇到的问题是,这个自定义属性在动画期间似乎与布局的其余部分稍微不同步 下面是一个简单的例子来说明我的意思: 蓝色方块使用内置的transform属性设置动画,而红色方块则在另一个(静止的)UIView中绘制,并通过自定义属性drawFrame设置动画。两个属性的动画都匹配,因此,理论上,两个矩形应该始终彼此垂直对齐 下面是上面示例的代码。要执行,只需启动一个新的单视图应用程序项
UIView
子类,它有一个自定义的可设置动画的属性,与其他视图上的其他(内置)UIView
属性一起设置动画。我遇到的问题是,这个自定义属性在动画期间似乎与布局的其余部分稍微不同步
下面是一个简单的例子来说明我的意思:
蓝色方块使用内置的transform
属性设置动画,而红色方块则在另一个(静止的)UIView
中绘制,并通过自定义属性drawFrame
设置动画。两个属性的动画都匹配,因此,理论上,两个矩形应该始终彼此垂直对齐
下面是上面示例的代码。要执行,只需启动一个新的单视图应用程序项目并替换ViewController.swift
的内容即可
import UIKit
class ViewController: UIViewController {
let viewA = UIView(frame: CGRect(x: 50, y: 50, width: 100, height: 100))
let viewB = CustomAnimatedPropertyView(frame: CGRect(x: 50, y: 150, width: 200, height: 100))
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(viewA); viewA.backgroundColor = UIColor.blue
view.addSubview(viewB); viewB.backgroundColor = UIColor.clear
animate()
}
func animate() {
viewA.transform = CGAffineTransform.identity
viewB.drawFrame = CGRect(x: 0, y: 0, width: 100, height: 100)
UIView.animate(withDuration: 2.0, delay: 1.0, options: [], animations: {
self.viewA.transform = CGAffineTransform.identity.translatedBy(x: 100, y: 0.0)
self.viewB.drawFrame = CGRect(x: 100, y: 0, width: 100, height: 100)
})
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 3) { self.animate() }
}
}
class CustomAnimatedPropertyView: UIView {
@objc dynamic var drawFrame: CGRect = CGRect.zero {
didSet {
guard let layer = layer as? CustomAnimatedPropertyLayer else { fatalError() }
layer.drawFrame = drawFrame
}
}
override public class var layerClass: AnyClass {
return CustomAnimatedPropertyLayer.self
}
override public func action(for layer: CALayer, forKey event: String) -> CAAction? {
if event == #keyPath(CustomAnimatedPropertyLayer.drawFrame),
let action = super.action(for: layer, forKey: #keyPath(backgroundColor)) as? CAAnimation,
let animation: CABasicAnimation = (action.copy() as? CABasicAnimation) {
animation.keyPath = #keyPath(CustomAnimatedPropertyLayer.drawFrame)
animation.fromValue = (layer as! CustomAnimatedPropertyLayer).drawFrame
animation.toValue = drawFrame
layer.add(animation, forKey: #keyPath(CustomAnimatedPropertyLayer.drawFrame))
return animation
}
return super.action(for: layer, forKey: event)
}
override func draw(_ layer: CALayer, in ctx: CGContext) {
guard let customLayer = layer as? CustomAnimatedPropertyLayer else { super.draw(layer, in: ctx); return }
ctx.setFillColor(UIColor.red.cgColor)
ctx.fill(customLayer.drawFrame)
usleep(20 * 1000) // <- To exacerbate the synchronization issue
}
}
class CustomAnimatedPropertyLayer: CALayer {
@NSManaged var drawFrame: CGRect
override class func needsDisplay(forKey key: String) -> Bool {
return super.needsDisplay(forKey: key) || key == #keyPath(drawFrame)
}
}
导入UIKit
类ViewController:UIViewController{
让viewA=UIView(帧:CGRect(x:50,y:50,宽度:100,高度:100))
let viewB=CustomAnimatedPropertyView(帧:CGRect(x:50,y:150,宽度:200,高度:100))
重写func viewDidLoad(){
super.viewDidLoad()
view.addSubview(viewA);viewA.backgroundColor=UIColor.blue
view.addSubview(viewB);viewB.backgroundColor=UIColor.clear
制作动画()
}
func animate(){
viewA.transform=CGAffineTransform.identity
viewB.drawFrame=CGRect(x:0,y:0,宽度:100,高度:100)
UIView.animate(持续时间:2.0,延迟:1.0,选项:[],动画:{
self.viewA.transform=CGAffineTransform.identity.translatedBy(x:100,y:0.0)
self.viewB.drawFrame=CGRect(x:100,y:0,宽度:100,高度:100)
})
DispatchQueue.main.asyncAfter(截止日期:DispatchTime.now()+3){self.animate()}
}
}
类CustomAnimatedPropertyView:UIView{
@objc动态变量drawFrame:CGRect=CGRect.zero{
迪塞特{
guard let layer=层为?CustomAnimatedPropertyLayer else{fatalError()}
layer.drawFrame=drawFrame
}
}
重写公共类var layerClass:AnyClass{
返回CustomAnimatedPropertyLayer.self
}
覆盖公共函数操作(对于层:CALayer,forKey事件:String)->CAAction{
如果事件=#关键路径(CustomAnimatedPropertyLayer.drawFrame),
让action=super.action(对于:层,forKey:#keyPath(backgroundColor))作为动画,
让动画:CABasicAnimation=(action.copy()作为?CABasicAnimation){
animation.keyPath=#keyPath(CustomAnimatedPropertyLayer.drawFrame)
animation.fromValue=(图层为!CustomAnimatedPropertyLayer)。drawFrame
animation.toValue=drawFrame
添加(动画,forKey:#关键路径(CustomAnimatedPropertyLayer.drawFrame))
返回动画
}
返回super.action(for:layer,forKey:event)
}
覆盖func绘图(layer:CALayer,在ctx:CGContext中){
guard let customLayer=图层为?CustomAnimatedPropertyLayer else{super.draw(图层,in:ctx);返回}
ctx.setFillColor(UIColor.red.cgColor)
ctx.fill(customLayer.drawFrame)
usleep(20*1000)//Bool{
返回super.needsDisplay(forKey:key)| | key==#keyPath(drawFrame)
}
}
我尝试过很多不同的解决方案,但都没有效果……我认为可以使用CADisplayLink
或类似的解决方案来解决这个问题,但我更倾向于避免涉及重大代码重构的解决方案
关于如何解决这个问题有什么想法吗?提前感谢您的回答。我认为您的问题没有通用的解决方案,动画的抖动来自不同的类型。转换动画应该比简单的标量值动画更复杂 然而,我看到了以下几种可能的方法来解决snchronization问题。我有几个建议供您选择
转换
属性进行转换。两个层都应使用通用的基本动画不幸的是,无可否认,我的建议非常笼统,但为了能够给出更具体的路径,我必须对具体问题有更多的了解。输入代码viewDidAppear@Sh_Khan同样的结果。我还应该指出,上面的示例代码是我正在处理的实际生产代码的一个非常简化的版本视图具有不同的Xposition@Sh_Khan我不知道你为什么这么说……蓝色视图是一个完美的100x100px正方形,它被转换为右100px。红色视图是一个200x100px的矩形,它根本不移动-只有在它里面绘制的矩形得到更新,从它的初始位置向右正好100px。原点X两个视图的坐标相同。@Kevinosaurio
drawFrame
的值在targetui视图中表示