2

I have a UIView and I set a shadowPath for it like this:

func addShadow() {
    let cornerRadius: CGFloat = self.containerView.frame.height/2
    self.containerView.layer.shadowPath = UIBezierPath(roundedRect: self.containerView.frame, cornerRadius: cornerRadius).cgPath
    self.containerView.layer.shadowRadius = cornerRadius
    self.containerView.layer.shadowOffset = .zero
    self.containerView.layer.shadowOpacity = 0.2
    self.containerView.layer.cornerRadius = cornerRadius
    self.containerView.layer.shadowColor = UIColor(named: "whiteColor")?.cgColor
}

And this is whiteColor:

enter image description here

And now my problem is

When I change the appearance of the phone, the shadowPath's color doesn't change automatically. What should I do to make the color dynamic?

Adil Hussain
  • 30,049
  • 21
  • 112
  • 147
Arash Etemad
  • 1,827
  • 1
  • 13
  • 29
  • Do all your color stuff in layoutSubview or viewDidLayoutSubviews. Then you set the color to a dynamic variable that will compute it's value based on the traitCollection.userInterfaceStyle – andromedainiative Sep 25 '19 at 07:04
  • @andromedainiative Are you sure it's the best way? I have many views that have shadow, Is it right that to set all of them in `layoutSubView`? – Arash Etemad Sep 25 '19 at 07:07
  • 1
    I mean there is no silver bullet generic solution that I know of. We ended up moving all our color setup these methods. You can read more about it here. Unless you use the same view controller in every view you have you will have to do some updating of colors manually. https://developer.apple.com/documentation/appkit/supporting_dark_mode_in_your_interface – andromedainiative Sep 26 '19 at 08:52
  • @andromedainiative nice document thanks – Arash Etemad Sep 26 '19 at 10:12

3 Answers3

13

All layer properties don't react to color changes automatically (due to the conversion to CGColor).

Instead, react to trait collection changes and re-set the properties when the color appearance changes:

override open func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
    super.traitCollectionDidChange(previousTraitCollection)

    if #available(iOS 13, *), self.traitCollection.hasDifferentColorAppearance(comparedTo: previousTraitCollection) {
        // re-set your properties here
    }
}
Frank Rupprecht
  • 9,191
  • 31
  • 56
  • Then I should override this method in every `ViewController`?! I need a more generic solution! – Arash Etemad Sep 25 '19 at 07:18
  • Only were you set layer properties. But yes, it's a pain. I created a `BaseView` that provides `UIColor` properties for all those layer properties and handles appearance changes. But now I ended up with 4 different `BaseView`s for buttons, table view cells, etc... – Frank Rupprecht Sep 25 '19 at 07:22
  • Please call super.traitCollectionDidChange – Umair Jul 21 '20 at 21:03
2

So this is what worked for me - it's a mix between Frank Schlegel's answer and between Kurt Revis's answer to the cgColor question


override open func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {

   if #available(iOS 13, *), self.traitCollection.hasDifferentColorAppearance(comparedTo: previousTraitCollection) {

       SubmissionOptionsMenu.layer.shadowColor = 
              UIColor.label.resolvedColor(with: self.traitCollection).cgColor
   }
}
Curtain Call LLC
  • 144
  • 1
  • 1
  • 11
0

add self.view.layoutIfNeeded() in your function.It might solve your issue. In alternative way you will add color in viewDidLayoutSubviews.

Nahid Raihan
  • 957
  • 1
  • 10
  • 20