简体   繁体   中英

CALayer during animation is using default property value instead of what should be the current instance's property value

I've recreated my problem in a xCode Playground that samples the bug I'm running into (showing tends to be better at explaining): https://gist.github.com/Wowserman/c1910523ad85cf34d3e08150b65c7074

My problem is this, my CALayer class renders it's drawing perfectly, in this .gif you can see what it is when it is first rendered (Green with a shadow at the bottom) and to be clear that renders how it should, but as soon as I start animating the shadow height is where the problem is.

I animated the shadowHeight from 12.0 to 0.0 and reverse that animation, and well the animation works except the color property manages to change to the default value (Yellow) without my intent!

You can see in this gif, the layer is first rendered in, Green with the shadow (which is intended), then when the view is touched and the layer animates the shrink of the shadow (intended) but the color changes to the default value (Yellow) during the animation and once it is complete it goes back to the instance's proper color value. The animation should be the color 's value at all times throughout the animation.

在此处输入图片说明

I checked that the color property is never changed, I have a didSet observer printing every time it is changed, and it is only changed during initialization which is how it should be.

I then checked in the draw(in ctx:) method of CALayer and that's where I saw the color property was different.

Another possible problem I thought could be that the layer drawsAsynchronously is true, but after toggling it I identified it as not the problem.

My Problem Summed Up :

The layer's color property is discreetly changing to the default property value during the animation instead of the value set in initialization causing unwanted rendering.

I figured out the solution for anyone in need. When a CALayer is animated, the CALayer isn't mutated, rather it is duplicated by using CALayer's init(layer:) .

I was reading the documentation on this initializer ( enter link description here ) and found that inside the override you are suppose to copy layer 's custom properties to the new CALayer subclass.

public override init(layer: Any) {
    super.init(layer: layer)

    self.common()

    guard let overlay = layer as? OverlayLayer else {
        return
    }

    self.color = overlay.color
}

Here's an update Gist with the fix if you're curious

https://gist.github.com/Wowserman/ca23d9596f2979effd9cc8fa9e73c893

In your didSet observer on color - don't call display()

You're not meant to call display directly. As the docs state:

Do not call this method directly. The layer calls this method at appropriate times to update the layer's content.

Instead, use:

self.setNeedsDisplay()

Edit: The same goes for your common() function.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM