CAGradientLayer messes up CATransform3DRotate

I am observing a strange behaviour in a CATransform3DRotate which I am applying to the layer of a view which is in front of a view whose layer has a CAGradientLayer.

When the gradient view is there, the transform animation kind of masks half of the view I am transforming and everything else in front of it too. If the gradient view is not there or does not sit behind the view, everything is fine.

Here's screenshots to show what I mean. You can clearly see how the rotated view is partially hidden and the slider below too.

normal state flipped left flipped right

Since it looks like the gradient layer is on a different z-plane, I played around with it's z position but that didn't help. Also that still wouldn't explain to me how the completely unrelated UISlider is masked as well.

What am I doing wrong here?

Below is a completely self-contained UIViewController which illustrates my problem.

class TestViewController: UIViewController {
    private var gradientLayer: CAGradientLayer!
    private var flipContent: UILabel!
    private var flipView: UIView!
    private var gradientView: UIView!
    private var slider: UISlider!

    override func viewDidLoad() {
        super.viewDidLoad()
        setupViews()
        setupGradientLayer()
    }

    @IBAction func didChangeValue(_ sender: UISlider) {
        var perspectiveTransform = CATransform3DIdentity
        perspectiveTransform.m34 = -0.002
        let rotateAngle = CGFloat(sender.value) * CGFloat(Double.pi)
        let flipTransform = CATransform3DRotate(perspectiveTransform, rotateAngle, 0, 1, 0)
        flipView.layer.transform = flipTransform
    }

    private func setupGradientLayer() {
        gradientLayer = CAGradientLayer()
        gradientLayer.colors = [
            UIColor(red: 74 / 255, green: 173 / 255, blue: 255 / 255, alpha: 1.0).cgColor,
            UIColor(red: 0 / 255, green: 94 / 255, blue: 215 / 255, alpha: 1.0).cgColor
        ]
        gradientLayer.frame = view.bounds
        gradientView.layer.insertSublayer(gradientLayer, at: 0)
    }

    private func setupViews() {
        gradientView = UIView(frame: CGRect(x: 10, y: 150, width: 200, height: 30))
        slider = UISlider(frame: CGRect(x: 10, y: 400, width: 200, height: 30))
        slider.minimumValue = 0
        slider.maximumValue = 1
        slider.addTarget(self, action: #selector(didChangeValue(_:)), for: .valueChanged)
        flipView = UIView(frame: CGRect(x: 10, y: 100, width: 200, height: 200))
        flipView.backgroundColor = UIColor.lightGray
        flipContent = UILabel(frame: CGRect(x: 10, y: 100, width: 100, height: 100))
        flipContent.text = "I will flip"

        flipView.addSubview(flipContent)
        view.addSubview(gradientView)
        view.addSubview(flipView)
        view.addSubview(slider)
    }
}

1 answer

  • answered 2018-01-14 11:31 GaétanZ

    Add a z translation to your transform :

    var perspectiveTransform = CATransform3DIdentity
    perspectiveTransform.m34 = -0.002
    let rotateAngle = CGFloat(sender.value) * CGFloat(Double.pi)
    perspectiveTransform = CATransform3DTranslate(perspectiveTransform, 0, 0, 300)
    flipView.layer.transform = CATransform3DRotate(perspectiveTransform, rotateAngle, 0, 1, 0)
    

    If you take a look at the Debug View Hierarchy, you can see that your views are in the same plan.

    You can also set the zPosition (which is a kind of shorthand for z translation) :

    flipView.layer.zPosition = 300