Forgotten UIKit gems: UIAppearance

fmo91

Fernando Martín Ortiz

Posted on March 27, 2021

Forgotten UIKit gems: UIAppearance

What is UIAppearance?

According to Apple docs, UIAppearance is "A collection of methods that gives you access to the appearance proxy for a class.".
In simple words, you can modify the UIAppearance object that is related to a certain view, and all of the views of that type will be affected. As simple and powerful (and dangerous) as that.
There is another possibility. You can specify that only the views contained in a parent view of type T will be affected by the appearance change.
Important note: the UIAppearance customization needs to be performed before the views have entered in the UIWindow. Otherwise, they won't be affected.

A practical example

Let's suppose we have this UI:

Simulator Screen Shot - iPhone 11 - 2021-03-27 at 15.20.54

We can change the properties of all the UILabel objects in that UI by changing their UIAppearance. Let's put this in the AppDelegate application(_:didFinishLaunchingWithOptions:) method:

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    UILabel.appearance().textColor = UIColor.systemGreen
    return true
}
Enter fullscreen mode Exit fullscreen mode

Simulator Screen Shot - iPhone 11 - 2021-03-27 at 15.28.40

You can see how simple it is to update the properties in a view using UIAppearance.

You may be wondering why the UILabel inside UIButton haven't been affected. The reason is that those labels are of type UIButtonLabel.

In the case we don't want the UILabel inside UITextField to be affected by the changes in the UILabel appearance, we can't do this:

UILabel.appearance(whenContainedInInstancesOf: [UITextField.self]).textColor = UIColor.lightGray
Enter fullscreen mode Exit fullscreen mode

Simulator Screen Shot - iPhone 11 - 2021-03-27 at 15.46.39

How it saved my life this week

To be honest, I've completely forgotten of UIAppearance until this week it saved my project.
I had a WKWebView where some scroll view indicators were randomly appearing in the middle of the screen.

I wasn't the only one experiencing this problems, as it was also happening in projects like React Native and Flutter.

Not only that. When I tried to debug the view hierarchy in my WKWebView, I found that it was creating other UIScrollView subclasses inside of it. And webView.scrollView only affected the topmost scroll view.

I tried everything I found in the Github issues I've seen, but nothing worked. Then I remembered UIAppearance, and did something like this:

UIScrollView
    .appearance(whenContainedInInstancesOf: [MyWebView.self])
    .scrollIndicatorInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 1)
Enter fullscreen mode Exit fullscreen mode

Adding that immediately fixed all the scroll view inside the web view.

Caveats

While powerful, applying UIAppearance everywhere is a VERY DANGEROUS anti-pattern you should be aware of. It's an extremely powerful tool when it's required, but shouldn't be used more than that.

💖 💪 🙅 🚩
fmo91
Fernando Martín Ortiz

Posted on March 27, 2021

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related