Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
160 views
in Technique[技术] by (71.8m points)

ios - Some views with a CAGradientLayer don't render the gradient

I've been facing some difficulties trying to apply CAGradientLayers on two items in a view controller: a UIView and a UIButton. On investigation, when the gradient is applied to both items, only the UIButton has the gradient on it whereas the UIView appears transparent.

My gradient is defined as such:

import UIKit

struct K {
    struct Design {
        static let upGradientLayer: CAGradientLayer = {
            let layer = CAGradientLayer()
            layer.colors = [upBlue.cgColor, upPurple.cgColor]
            layer.startPoint = CGPoint(x: 0.0, y: 0.0)
            layer.endPoint = CGPoint(x: 1.0, y: 1.0)
            return layer
        }()

        static let upBlue = UIColor(named: "UP Blue") ?? .systemBlue
        static let upPurple = UIColor(named: "UP Purple") ?? .systemPurple
    }
}

The function that applies the gradients (I used insertSublayer) is separated from the main View Controller file. Here's the code for that function:

import UIKit

extension UIButton {
    func applyButtonDesign(_ gradientLayer: CAGradientLayer) {
        self.layer.insertSublayer(gradientLayer, at: 0)
        self.layer.cornerRadius = 10.0
        self.layer.masksToBounds = true
    }
}

extension UIView {
    func applyHeaderDesign(_ gradientLayer: CAGradientLayer) {
        self.layer.insertSublayer(gradientLayer, at: 0)
        self.layer.cornerRadius = 10.0
        self.layer.maskedCorners = [.layerMinXMaxYCorner, .layerMaxXMaxYCorner]
        self.clipsToBounds = true
    }
}

(Note: It appears that addSublayer doesn't work either.)

My UI is being created programmatically (without a Storyboard), and I'm fairly new to it. Here's the code for the view controller where the issue is happening:

import UIKit

class WelcomeViewController: UIViewController {

    var headerView: UIView!
    var descriptionLabel: UILabel!
    var focusImageView: UIImageView!
    var promptLabel: UILabel!
    var continueButton: UIButton!

    let headerGradientLayer = K.Design.upGradientLayer
    let buttonGradientLayer = K.Design.upGradientLayer

    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .systemBackground

        let guide = view.safeAreaLayoutGuide

        // MARK: Header View
        headerView = UIView()
        headerView.translatesAutoresizingMaskIntoConstraints = false
        headerView.applyHeaderDesign(headerGradientLayer)
        view.addSubview(headerView)
        NSLayoutConstraint.activate([
            headerView.topAnchor.constraint(equalTo: view.topAnchor),
            headerView.rightAnchor.constraint(equalTo: view.rightAnchor),
            headerView.leftAnchor.constraint(equalTo: view.leftAnchor),
            headerView.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 0.25)
        ])

        // MARK: Other items in ViewController

        // MARK: Continue Button
        continueButton = UIButton()
        continueButton.translatesAutoresizingMaskIntoConstraints = false
        continueButton.applyButtonDesign(buttonGradientLayer)
        continueButton.setTitle("Let's go!", for: .normal)
        continueButton.titleLabel?.font = UIFont(name: "Metropolis Bold", size: 22.0)
        continueButton.titleLabel?.textColor = .white
        continueButton.addTarget(nil, action: #selector(continueButtonPressed), for: .touchUpInside)
        view.addSubview(continueButton)
        NSLayoutConstraint.activate([
            continueButton.topAnchor.constraint(equalTo: promptLabel.bottomAnchor, constant: K.Layout.someSpaceBetween),
            continueButton.rightAnchor.constraint(equalTo: view.rightAnchor, constant: -K.Layout.someSpaceBetween),
            continueButton.leftAnchor.constraint(equalTo: view.leftAnchor, constant: K.Layout.someSpaceBetween),
            continueButton.bottomAnchor.constraint(equalTo: guide.bottomAnchor),
            continueButton.heightAnchor.constraint(equalToConstant: 50.0)
        ])
    }

    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()

        headerGradientLayer.frame = headerView.bounds
        buttonGradientLayer.frame = continueButton.bounds
    }

    // MARK: - Functions
    @objc func continueButtonPressed() {
        let newViewController = NameViewController()
        newViewController.modalPresentationStyle = .fullScreen
        newViewController.hero.modalAnimationType = .slide(direction: .left)
        self.present(newViewController, animated: true, completion: nil)
    }

}

When running on the Simulator, I get the below image that shows the issue. Notice that the gradient is not applied on the UIView but is on the UIButton.

image of issue with gradient not applied on the UIView but is on the UIButton

What's causing this issue to happen, and how can I resolve this? If there's a fundamental concept that needs to be learned to tackle this issue, do share as well.

question from:https://stackoverflow.com/questions/65649803/some-views-with-a-cagradientlayer-dont-render-the-gradient

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Both headerGradientLayer and buttonGradientLayer refer to the same layer instance because upGradientLayer is a stored property, not a computed property (as I'm assuming you meant for it to be). Since they both refer to the same instance, when you call this:

continueButton.applyButtonDesign(buttonGradientLayer)

...which internally calls this:

self.layer.insertSublayer(gradientLayer, at: 0)

...it removes the gradient layer instance from its current view (your header view) and adds it to your button (because layers cannot have more than one superlayer). The result is that you only see the gradient on your button and not your header view.

I imagine you meant for upGradientLayer to be a computed property so that it returns a new layer instance every time it's called, like this:

static var upGradientLayer: CAGradientLayer {
    let layer = CAGradientLayer()
    layer.colors = [upBlue.cgColor, upPurple.cgColor]
    layer.startPoint = CGPoint(x: 0.0, y: 0.0)
    layer.endPoint = CGPoint(x: 1.0, y: 1.0)
    return layer
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...