Reputation: 21
I've been facing some difficulties trying to apply CAGradientLayer
s 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
.
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.
Upvotes: 1
Views: 75
Reputation: 9829
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
}
Upvotes: 1