Creating a reusable UIView with xib (and loading from storyboard)

前端 未结 6 1051
孤独总比滥情好
孤独总比滥情好 2020-11-28 18:49

OK, there are dozens of posts on StackOverflow about this, but none are particularly clear on the solution. I\'d like to create a custom UIView with an accompan

6条回答
  •  野性不改
    2020-11-28 19:16

    I'm adding this as a separate post to update the situation with the release of Swift. The approach described by LeoNatan works perfectly in Objective-C. However, the stricter compile time checks prevent self being assigned to when loading from the xib file in Swift.

    As a result, there is no option but to add the view loaded from the xib file as a subview of the custom UIView subclass, rather than replacing self entirely. This is analogous to the second approach outlined in the original question. A rough outline of a class in Swift using this approach is as follows:

    @IBDesignable // <- to optionally enable live rendering in IB
    class ExampleView: UIView {
    
        required init(coder aDecoder: NSCoder) {
            super.init(coder: aDecoder)
            initializeSubviews()
        }
    
        override init(frame: CGRect) {
            super.init(frame: frame)
            initializeSubviews()
        }
    
        func initializeSubviews() {
            // below doesn't work as returned class name is normally in project module scope
            /*let viewName = NSStringFromClass(self.classForCoder)*/
            let viewName = "ExampleView"
            let view: UIView = NSBundle.mainBundle().loadNibNamed(viewName,
                                   owner: self, options: nil)[0] as! UIView
            self.addSubview(view)
            view.frame = self.bounds
        }
    
    }
    

    The downside of this approach is the introduction of an additional redundant layer in the view hierarchy which does not exist when using the approach outlined by LeoNatan in Objective-C. However, this could be taken as a necessary evil and a product of the fundamental way things are designed in Xcode (it still seems crazy to me that it is so difficult to link a custom UIView class with a UI layout in a way that works consistently over both storyboards and from code) – replacing self wholesale in the initializer before never seemed like a particularly interpretable way of doing things, although having essentially two view classes per view doesn't seem so great either.

    Nonetheless, one happy result of this approach is that we no longer need to set the view's custom class to our class file in interface builder to ensure correct behaviour when assigning to self, and so the recursive call to init(coder aDecoder: NSCoder) when issuing loadNibNamed() is broken (by not setting the custom class in the xib file, the init(coder aDecoder: NSCoder) of plain vanilla UIView rather than our custom version will be called instead).

    Even though we cannot make class customizations to the view stored in the xib directly, we are still able to link the view to our 'parent' UIView subclass using outlets/actions etc. after setting the file owner of the view to our custom class:

    Setting the file owner property of the custom view

    A video demonstrating the implementation of such a view class step by step using this approach can be found in the following video.

提交回复
热议问题