Swift subclass UIView

后端 未结 6 1857
时光说笑
时光说笑 2020-12-07 10:49

I want to subclass UIView and show a login like view. I\'ve created this in Objective-C, but now I want to port it to Swift. I do not use storyboards, so I crea

6条回答
  •  佛祖请我去吃肉
    2020-12-07 11:24

    Custom UIView Subclass Example

    I usually create iOS apps without using storyboards or nibs. I'll share some techniques I've learned to answer your questions.

     

    Hiding Unwanted init Methods

    My first suggestion is to declare a base UIView to hide unwanted initializers. I've discussed this approach in detail in my answer to "How to Hide Storyboard and Nib Specific Initializers in UI Subclasses". Note: This approach assumes you will not use BaseView or its descendants in storyboards or nibs since it will intentionally cause the app to crash.

    class BaseView: UIView {
    
        // This initializer hides init(frame:) from subclasses
        init() {
            super.init(frame: CGRect.zero)
        }
    
        // This attribute hides `init(coder:)` from subclasses
        @available(*, unavailable)
        required init?(coder aDecoder: NSCoder) {
            fatalError("NSCoding not supported")
        }
    }
    

    Your custom UIView subclass should inherit from BaseView. It must call super.init() in its initializer. It does not need to implement init(coder:). This is demonstrated in the example below.

     

    Adding a UITextField

    I create stored properties for subviews referenced outside of the init method. I would typically do so for a UITextField. I prefer to instantiate subviews within the declaration of the subview property like this: let textField = UITextField().

    The UITextField will not be visible unless you add it to the custom view's subview list by calling addSubview(_:). This is demonstrated in the example below.

     

    Programmatic Layout Without Auto Layout

    The UITextField will not be visible unless you set its size and position. I often do layout in code (not using Auto Layout) within the layoutSubviews method. layoutSubviews() is called initially and whenever a resize event happens. This allows adjusting layout depending on the size of CustomView. For example, if CustomView appears the full width on various sizes of iPhones and iPads and adjusts for rotation, it needs to accommodate many initial sizes and resize dynamically.

    You can refer to frame.height and frame.width within layoutSubviews() to get the CustomView's dimensions for reference. This is demonstrated in the example below.

     

    Example UIView Subclass

    A custom UIView subclass containing a UITextField which does not need to implement init?(coder:).

    class CustomView: BaseView {
    
        let textField = UITextField()
    
        override init() {
            super.init()
    
            // configure and add textField as subview
            textField.placeholder = "placeholder text"
            textField.font = UIFont.systemFont(ofSize: 12)
            addSubview(textField)
        }
    
        override func layoutSubviews() {
            super.layoutSubviews()
    
            // Set textField size and position
            textField.frame.size = CGSize(width: frame.width - 20, height: 30)
            textField.frame.origin = CGPoint(x: 10, y: 10)
        }
    }
    

     

    Programmatic Layout with Auto Layout

    You can also implement layout using Auto Layout in code. Since I don't often do this, I will not show an example. You can find examples of implementing Auto Layout in code on Stack Overflow and elsewhere on the Internet.

     

    Programmatic Layout Frameworks

    There are open source frameworks that implement layout in code. One I am interested in but have not tried is LayoutKit. It was written by the development team an LinkedIn. From the Github repository: "LinkedIn created LayoutKit because we have found that Auto Layout is not performant enough for complicated view hierarchies in scrollable views."

     

    Why put fatalError in init(coder:)

    When creating UIView subclasses that will never be used in a storyboard or nib, you might introduce initializers with different parameters and initialization requirements that could not be called by the init(coder:) method. If you did not fail init(coder:) with a fatalError, it could lead to very confusing problems down the line if accidentally used in a storyboard/nib. The fatalError asserts these intentions.

    required init?(coder aDecoder: NSCoder) {
        fatalError("NSCoding not supported")
    }
    

    If you want to run some code when the subclass is created regardless of whether it is created in code or a storyboard/nib then you could do something like the following (based on Jeff Gu Kang’s answer)

    class CustomView: UIView {
        override init (frame: CGRect) {
            super.init(frame: frame)
            initCommon()
        }
    
        required init?(coder aDecoder: NSCoder) {
            super.init(coder: aDecoder)
            initCommon()
        }
    
        func initCommon() {
            // Your custom initialization code
        }
    }
    

提交回复
热议问题