简体   繁体   中英

Custom UIView is blank when instantiated from a XIB in Swift 4

I have a custom view that I am trying to load from a custom XIB, but the view appears to be blank when loaded, even thought it has the correct sizes when debugged.

My debug statements show that the frame has the correct sizes:

commonInit()
XIB: MyCustomView
myView Frame: (0.0, 0.0, 320.0,568.0)
myView ContentSize: (320.0, 710.0)

This is my custom VC that I am using to call my Custom View

class MyCustomViewController: UIViewController {

    var myView : MyCustomView!

    override func viewDidLoad() {
        super.viewDidLoad()

        myView = MyCustomView(frame: self.view.frame)

        self.view.addSubview(myView)

        updateScrollViewSize()

        print("myView Frame: \(myView.frame)")
        print("myView ContentSize: \(myView.contentView.contentSize)")

    }

    func updateScrollViewSize () {
        var contentRect = CGRect.zero
        for view in myView.contentView.subviews {
            contentRect = contentRect.union(view.frame)
        }
        myView.contentView.contentSize = CGSize(width: myView.contentView.frame.size.width, height: contentRect.size.height + 5)
    }
}

There is a XIB that has the files owner as MyCustomView and all the outlets are hooked up correctly.

class MyCustomView: UIView {

     let kCONTENT_XIB_NAME = "MyCustomView"

    @IBOutlet var contentView: UIScrollView!

    @IBOutlet weak var lbl_datein: UILabel!
    //.. A bunch of other GUI elements for the scrollview
    @IBOutlet weak var text_location: UITextField!


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

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

    func commonInit() {
        print(#function)
        print("XIB: \(kCONTENT_XIB_NAME)")

        Bundle.main.loadNibNamed(kCONTENT_XIB_NAME, owner: self, options: nil)
        contentView.addSubview(self)
        contentView.frame = self.bounds
        contentView.backgroundColor = .blue
    }
}

Does anyone see what I have done wrong when trying to load the view

I'm going to post an alternative to what you've done, using an extension.

extension UIView {

    @discardableResult
    func fromNib<T : UIView>(_ nibName: String? = nil) -> T? {
        let bundle = Bundle(for: type(of: self))
        guard let view = bundle.loadNibNamed(nibName ?? String(describing: type(of: self)), owner: self, options: nil)?[0] as? T else {
            return nil
        }
        view.translatesAutoresizingMaskIntoConstraints = false

        self.addSubview(view)
        view.autoPinEdgesToSuperviewEdges()
        return view
    }
}

*Note that I am using PureLayout for convenient autolayout management, you could just apply the constraints manually yourself though if you aren't using PureLayout.

Using the above all you have to do is call the below from your init;

fromNib()

*Final note. The custom view name must match the nib name, otherwise you must pass the nib name in to you fromNib function.

You now have something much more reusable.

I couldn't get this to run copy/pasting the code. Maybe there's some setup missing, but I'm having a hard time understanding how it's supposed to work. The original code in the question crashes on this line:

contentView.addSubview(self)

because when you have IBOutlets , they will always be nil if you initialize it using MyCustomView(frame: self.view.frame) . It has to call the initWithCoder function.

There's a lot going on here, but this is how I would do it:

class MyCustomViewController: UIViewController {

    var myView: MyCustomView!

    override func viewDidLoad() {
        super.viewDidLoad()

        myView = Bundle.main.loadNibNamed("MyCustomView", owner: self, options: nil)?.first as? MyCustomView

        self.view.addSubview(myView)

        updateScrollViewSize()

        print("myView Frame: \(myView.frame)")
        print("myView ContentSize: \(myView.contentView.contentSize)")

    }

    func updateScrollViewSize () {
        var contentRect = CGRect.zero
        for view in myView.contentView.subviews {
            contentRect = contentRect.union(view.frame)
        }
        myView.contentView.contentSize = CGSize(width: myView.contentView.frame.size.width, height: contentRect.size.height + 5)
    }

}

class MyCustomView: UIView {

    let kCONTENT_XIB_NAME = "MyCustomView"

    @IBOutlet var contentView: UIScrollView!

    @IBOutlet weak var lbl_datein: UILabel!
    //.. A bunch of other GUI elements for the scrollview
    @IBOutlet weak var text_location: UITextField!

}

I'm assuming that the top-level object in the nib is of class MyCustomView , which is going to lead to a lot of weird things. loadNibNamed will call init?(coder aDecoder: NSCoder) , so ideally you'd just be calling that from your view controller in the first place, instead of from the custom view object.

With regards to the "can't add self as subview" error, I did not see that error while running, but I would expect it from this line:

contentView.addSubview(self)

since that's exactly what it does, add self as a subview of a view that's already a subview of self .

If my alternative answer is too much, let me try solve your existing issue. Instead of the below;

Bundle.main.loadNibNamed(kCONTENT_XIB_NAME, owner: self, options: nil)
contentView.addSubview(self)

Try;

let nibView = Bundle.main.loadNibNamed(kCONTENT_XIB_NAME, owner: self, options: nil)
self.addSubView(nibView)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM