Don’t subclass UIKit controls (e.g. UIButton, UILabel, UITextField and UISlider).

UIKit is built on composition, and this fact is reflected throughout its API. Notice how almost none of the methods on UIKit controls require subclassing. This is by design.

When you subclass a UIKit control, you’re forced to make assumptions about the internals of that control and how it behaves. These internals could change from one version of iOS to another, causing subtle issues that are difficult to reproduce.

What’s more, some UIKit controls (such as UIButton) have initializers that are implemented as factory methods that vend private types. This means that your custom initialization code won’t even be run.

Instead of subclassing UIKit controls, opt for one of the following approaches:

Configuration

If your requirements can be achieved by styling the component using its public API, use that when you declare the variable . By assigning a default value using a closure, you can neatly organize configuration outside of your lifecycle methods.

For example, don’t do:

class MyButton: UIButton {
    override init(frame: CGRect) {
        super.init(frame: frame)

titleLabel?.font = UIFont.systemFont(ofSize: 15.0)
        setTitleColor(.black, for: .normal)
        layer.cornerRadius = 5
        layer.masksToBounds = true
    }
}

Instead do:

let loginButton : UIButton = {
    let button = UIButton()
    button.titleLabel?.font = UIFont.systemFont(ofSize: 15.0)
    button.setTitleColor(.black, for: .normal)
    button.layer.cornerRadius = 5
    button.layer.masksToBounds = true

return button
}()

If your styles are often reused throughout your application, keep them organized in a single place and avoid code repition.

Customization

Sometimes, you need to compose multiple views together in order to achieve the desired result. If this is the case, subclass UIView and make your changes in there. Do not attempt to modify the view hierarchy of a UIKit control.

For example, if you want your UITextField to display an error message when validation fails on its contents, create your own subclass of UIView that will contain both the text field and the error label. Do not attempt to add the error label directly to the UITextField’s view hierarchy.

In the case where you need to change the behaviour of a UIKit component without adding additional views, the same advice applies: create a subclass of UIView and implement the new behaviour in there.

For example, if you want to create a UIButton that toggles between two states, create your own UIView subclass to wrap the UIButton instead of adding that behaviour to a UIButton subclass directly.

Exceptions

There are situations where subclassing is necessary, which revolves around API that require subclassing. You may subclass UIKit controls in order to use this API, but the above guidelines still apply; configuration and customization that can be added outside of the subclassed component should remain outside.

layoutSubviews()

layoutSubviews() is your opportunity to make changes to your view and its hierarchy after receiving a frame, but before drawing to the screen. In the rare instances where you need your code to run during the layout process, you may need to subclass a UIKit control in order to override this method.

UINavigationBar

You may need to subclass UINavigationBar and supply the custom subclass to UINavigationController in order to customize certain behaviours.

Drawing Text and content with UIButton, UILabel and UITextField

UIButton, UILabel and UITextField have some custom drawing API that relies on subclassing. The methods are as follows:

class UIButton {
    func backgroundRect(forBounds: CGRect) -> CGRect
    func contentRect(forBounds: CGRect) -> CGRect
    func titleRect(forContentRect: CGRect) -> CGRect
    func imageRect(forContentRect: CGRect) -> CGRect
}

class UITextField {
    func textRect(forBounds: CGRect) -> CGRect
    func drawText(in: CGRect)
    func placeholderRect(forBounds: CGRect) -> CGRect
    func drawPlaceholder(in: CGRect)
    func borderRect(forBounds: CGRect) -> CGRect
    func editingRect(forBounds: CGRect) -> CGRect
    func clearButtonRect(forBounds: CGRect) -> CGRect
    func leftViewRect(forBounds: CGRect) -> CGRect
    func rightViewRect(forBounds: CGRect) -> CGRect
}

class UILabel {
    func drawText(in rect: CGRect)
}

Disqus Recommendations

We were unable to load Disqus Recommendations. If you are a moderator please see our troubleshooting guide.

Disqus Comments

We were unable to load Disqus. If you are a moderator please see our troubleshooting guide.

G

Join the discussion…

Comment

Log in with
or sign up with Disqus or pick a name

Disqus is a discussion network

  • Don't be a jerk or do anything illegal. Everything is easier that way.

Read full terms and conditions

This comment platform is hosted by Disqus, Inc. I authorize Disqus and its affiliates to:

  • Use, sell, and share my information to enable me to use its comment services and for marketing purposes, including cross-context behavioral advertising, as described in our Terms of Service and Privacy Policy, including supplementing that information with other data about me, such as my browsing and location data.
  • Contact me or enable others to contact me by email with offers for goods or services
  • Process any sensitive personal information that I submit in a comment. See our Privacy Policy for more information

Acknowledge I am 18 or older

Favoriting means this is a discussion worth sharing. It gets shared to your followers' Disqus feeds, and gives the creator kudos!

Find More Discussions

Share

T

Hi Frank,

As you said, don't subclass a UIKit control ( like the UIButton in your first example). The alternative you propose is to give default values in a closure. Just for clarification, if we did want to make the button reusable, we would still wrap it in a UIView using the initializer's parameters to pass in the elements that change button to button? Usually I would just subclass the UIButton, UILabel, etc...

Thanks for the insight. Much appreciated!

- Tyler

see more

Hi Tyler!

Instead of subclassing the control, I think the best approach would be to create an extension (for example, on UIButton) and add a factory method ( like static func myButton() -> UIButton). This would allow you to not repeat all the setup code for using a button styled in a particular way throughout your application.

If you have any other questions, poke me on Twitter!

see more

T

Frank, thanks for the reply. That makes sense. I'll give it a go!

see more

Show more replies

Great post!

Do you have any open source projects where you might see these concepts in action?

see more

Load more comments

live.rezync.com

live.rezync.com is blocked

This page has been blocked by an extension

  • Try disabling your extensions.

ERR_BLOCKED_BY_CLIENT

Reload

This page has been blocked by an extension

pippio.com

pippio.com is blocked

This page has been blocked by an extension

  • Try disabling your extensions.

ERR_BLOCKED_BY_CLIENT

Reload

This page has been blocked by an extension