Reputation: 399
I want to change the gradient color in dark mode. But it not changing. Gradient set once the view is setup. Color not changing while switching between light and dark mode.
if let layer = layer as? CAGradientLayer {
if let startGradientColor = startGradientColor, let endGradientColor = endGradientColor {
layer.colors = [startGradientColor.cgColor, endGradientColor.cgColor]
} else {
layer.colors = gradientColors.map {$0.cgColor}
}
layer.startPoint = CGPoint(x: 0, y: 0) // top
layer.endPoint = CGPoint(x: 1, y: 1) // bottom
}
Upvotes: 3
Views: 2820
Reputation: 2935
This is what I did, works great.
SomeGradientView.swift
class SomeGradientView: UIView {
override open class var layerClass: AnyClass { return GradientLayer.self }
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
if #available(iOS 13.0, *) {
if traitCollection.hasDifferentColorAppearance(comparedTo: previousTraitCollection) {
(layer as? GradientLayer)?.resetGradientColors()
setNeedsDisplay()
}
}
}
}
GradientLayer.swift
class GradientLayer: CAGradientLayer {
// MARK: - Lifecycle
override init() {
super.init()
setup()
}
override init(layer: Any) {
super.init(layer: layer)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
// MARK: - Private
private func setup() {
// gradient setup
resetGradientColors()
locations = ...
opacity = 1
}
// MARK: - Internal
/// Used for trait collection color appearance changes
func resetGradientColors() {
colors = [UIColor.systemRed, UIColor.systemBlue]
}
}
Upvotes: 0
Reputation: 77477
You can do this with "Dynamic Providers"
For example, in your custom gradient view class:
var startGradientColorDarkMode: UIColor = .white
var startGradientColorLightMode: UIColor = .black
lazy var myStartColor = UIColor { (traitCollection: UITraitCollection) -> UIColor in
if traitCollection.userInterfaceStyle == .dark {
return self.startGradientColorDarkMode
} else {
return self.startGradientColorLightMode
}
}
You can then use myStartColor.cgColor
in your gradient colors array.
Here is a complete example:
class MyGradientView: UIView {
// default Dark mode - white to orange
var startGradientColorDarkMode: UIColor = .white
{
didSet {
self.setNeedsLayout()
}
}
var endGradientColorDarkMode: UIColor = .orange
{
didSet {
self.setNeedsLayout()
}
}
// default Light mode - black to blue
var startGradientColorLightMode: UIColor = .black
{
didSet {
self.setNeedsLayout()
}
}
var endGradientColorLightMode: UIColor = .blue
{
didSet {
self.setNeedsLayout()
}
}
lazy var myStartColor = UIColor { (traitCollection: UITraitCollection) -> UIColor in
if traitCollection.userInterfaceStyle == .dark {
return self.startGradientColorDarkMode
} else {
return self.startGradientColorLightMode
}
}
lazy var myEndColor = UIColor { (traitCollection: UITraitCollection) -> UIColor in
if traitCollection.userInterfaceStyle == .dark {
return self.endGradientColorDarkMode
} else {
return self.endGradientColorLightMode
}
}
private var gradientLayer: CAGradientLayer!
override class var layerClass: AnyClass {
return CAGradientLayer.self
}
override func layoutSubviews() {
self.gradientLayer = self.layer as? CAGradientLayer
self.gradientLayer.colors = [myStartColor.cgColor, myEndColor.cgColor]
self.gradientLayer.startPoint = CGPoint(x: 0, y: 0) // top
self.gradientLayer.endPoint = CGPoint(x: 1, y: 1) // bottom
}
}
class GradientTestViewController: UIViewController {
let testView = MyGradientView(frame: CGRect(x: 40, y: 100, width: 240, height: 240))
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(testView)
let tapGR = UITapGestureRecognizer(target: self, action: #selector(didTap))
self.view.addGestureRecognizer(tapGR)
// just for example
// change colors from our defaults
// change dark mode to red-to-yellow
testView.startGradientColorDarkMode = .red
testView.endGradientColorDarkMode = .yellow
// change light mode to darkbown-to-lightBrown
testView.startGradientColorLightMode = UIColor(red: 0.45, green: 0.3, blue: 0.25, alpha: 1.0)
testView.endGradientColorLightMode = UIColor(red: 0.8, green: 0.65, blue: 0.3, alpha: 1.0)
}
@objc func didTap(tapGR: UITapGestureRecognizer) {
// we can change the gradient colors on-the-fly
if testView.startGradientColorDarkMode == UIColor.red {
// change dark mode to blue-to-cyan
testView.startGradientColorDarkMode = .blue
testView.endGradientColorDarkMode = .cyan
// change light mode to darkGreen-to-lightGreen
testView.startGradientColorLightMode = UIColor(red: 0.0, green: 0.5, blue: 0.0, alpha: 1.0)
testView.endGradientColorLightMode = .green
} else {
// change dark mode to red-to-yellow
testView.startGradientColorDarkMode = .red
testView.endGradientColorDarkMode = .yellow
// change light mode to darkbown-to-lightBrown
testView.startGradientColorLightMode = UIColor(red: 0.45, green: 0.3, blue: 0.25, alpha: 1.0)
testView.endGradientColorLightMode = UIColor(red: 0.8, green: 0.65, blue: 0.3, alpha: 1.0)
}
}
}
Upvotes: 0
Reputation: 17844
You need to make your app react to changes to the trait collection, something like this:
extension YourViewController {
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
super.traitCollectionDidChange(previousTraitCollection)
if #available(iOS 13.0, *) {
guard traitCollection.hasDifferentColorAppearance(comparedTo: previousTraitCollection) else {
return
}
// redraw your layers here
}
}
}
Upvotes: 2