0

Assume I have a custom UIView class ColorWheelView.swift and XIB ColorWheelView.xib.

To create an custom UIView from XIB via code, here's the common practice


Via Code

extension UIView {
    static func instanceFromNib() -> Self {
        return getUINib().instantiate(withOwner: self, options: nil)[0] as! Self
    }
    
    static func getUINib() -> UINib {
        return UINib(nibName: String(describing: self), bundle: nil)
    }
}

// Create ColorWheelView from XIB.
let colorWheelView = ColorWheelView.instanceFromNib()

Via Storyboard (Doesn't look like a right way)

But, how about Storyboard? What if I use ColorWheelView as a subview in Storyboard? How can I inform Storyboard that ColorWheelView should be constructed directly from ColorWheelView.xib?

enter image description here

A common way I have seen so far is discussed in https://stackoverflow.com/a/34524346/72437 and https://stackoverflow.com/a/34524583/72437

import UIKit

class ColorWheelView: UIView {

    let nibName = "ColorWheelView"
    var contentView: UIView?

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        commonInit()
    }

    override init(frame: CGRect) {
        super.init(frame: frame)
        commonInit()
    }

    func commonInit() {
        guard let view = loadViewFromNib() else { return }
        view.frame = self.bounds
        self.addSubview(view)
        contentView = view
    }

    func loadViewFromNib() -> UIView? {
        let bundle = Bundle(for: type(of: self))
        let nib = UINib(nibName: nibName, bundle: bundle)
        return nib.instantiate(withOwner: self, options: nil).first as? UIView
    }
}

But, such code just doesn't look right to me. It merely

  1. Storyboard creates a "parent" ColorWheelView without using XIB.
  2. Then, "parent" ColorWheelView creates another "child" ColorWheelView from XIB, and used it as subview of itself.
  3. Doesn't seem like an optimised way, as now we are having 2 instances of ColorWheelView.

Is there a better way, to tell Storyboard that I want to create a custom subview from an XIB?

Jessy
  • 8,683
  • 5
  • 24
  • 38
Cheok Yan Cheng
  • 49,649
  • 117
  • 410
  • 768
  • 1
    It's just one more parent view. It won't make much difference in most cases. Are you actually having performance problems with this and found that having one fewer view improves the situation? The alternative to this is to get all the subviews from `nib.instantiate(withOwner: self, options: nil).first`, remove them from their superview, and then add them as subviews of `self`. The code is less convenient to write. – Sweeper Jan 24 '21 at 07:25
  • It is not performance issue. It is just doesn't feel right. Why do we ever need to create 2 instances of same custom views? – Cheok Yan Cheng Jan 24 '21 at 10:10
  • 1
    you are not creating two identical view hierarchies, just one extra superview. As I said in my comment, you don’t need to create the extra superview, but it’s more convenient to just do that. It’s a lot of trouble to re-add the sub views from the XIB to `self`. – Sweeper Jan 24 '21 at 10:19

1 Answers1

0
import UIKit

@IBDesignable class ColorWheelView: UIView {

let nibName = "ColorWheelView"
var contentView: UIView?

required init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
    commonInit()
}

override init(frame: CGRect) {
    super.init(frame: frame)
    commonInit()
}

func commonInit() {
    guard let view = loadViewFromNib() else { return }
    view.frame = self.bounds
    self.addSubview(view)
    contentView = view
}

func loadViewFromNib() -> UIView? {
    let bundle = Bundle(for: type(of: self))
    let nib = UINib(nibName: nibName, bundle: bundle)
    return nib.instantiate(withOwner: self, options: nil).first as? UIView
}
}

By putting designable keyword before class name will make this view to be used in storyboard way you want(by putting custom view class name in identity inspector.

Apps Maven
  • 705
  • 2
  • 9