Adrian Adrian - 4 months ago 46
Swift Question

Initializing a xib on a UIViewController's subview

I'm using DZNEmptySet to load when there's nothing to display in a UITableView. It works fine for DZNEmptySet's methods like

titleForEmptyDataSet
,
imageForEmptyDataSet
, but not the one I want to use (which is
customViewForEmptyDataSet
).

When I try to load the xib into the
scrollView.frame
, Xcode's memory starts bloating in 30 megabyte increments and the app hangs. I know I'm at fault, but I don't know what I'm mucking up.

I've looked at many answers here and tutorials on other sites, but I can't find a solution that works for this circumstance (which I think is pretty simple). Any feedback on that front would be greatly appreciated.

Here's
customViewForEmptyDataSet
on
MyViewController


// App hangs on this and memory bloats in 30 megabyte increments.
func customViewForEmptyDataSet(scrollView: UIScrollView!) -> UIView! {
return EmptySetView(frame: scrollView.frame)
}


Here's the class for my
EmptySetView
that I'm trying to initialize:

import UIKit

class EmptySetView: UIView {
var view: UIView!
// These are connected to a xib
@IBOutlet weak var backgroundImageView: UIImageView!
@IBOutlet weak var viewLabel: UILabel!
@IBOutlet weak var viewTextView: UITextView!

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

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

func setup() {
view = loadViewFromNib()
view.frame = bounds
view.autoresizingMask = [.FlexibleWidth, .FlexibleHeight]
self.addSubview(self.view)
}

func loadViewFromNib() -> UIView {
let bundle = NSBundle(forClass:self.dynamicType)
let nib = UINib(nibName: "EmptySetView", bundle: bundle)
let view = nib.instantiateWithOwner(self, options: nil)[0] as! UIView

return view
}
}


Update - Still no solution, but some progress

At Matt's suggestion, I investigated recursion I was experiencing and discovered part of the problem.

I discovered you do not specify the UIView class on the View for your xib. Instead, you click the yellow cube titled File's Owner and specify the
UIView
there, leaving the class field empty on the View in the Document Outline panel.

enter image description here

After cleaning caches and rebuilding, I can get the view to load in the hierarchy with this code called on
MyViewController
:

func customViewForEmptyDataSet(scrollView: UIScrollView!) -> UIView! {
let emptySetView = EmptySetView(frame: scrollView.frame)
print("scrollView.frame = \(scrollView.frame)")
print("emptySetView = \(emptySetView)")
return emptySetView
}


This is the console output:

scrollView.frame = (0.0, 209.0, 375.0, 458.0)
emptySetView = <WordCheat.EmptySetView: 0x7f84be3711c0; frame = (0 209; 375 458); layer = <CALayer: 0x7f84be3715c0>>


I looked at the Debug View Hierarchy and I see the my
EmptySetView
, but it's not filling out the
scrollView
that I specified in the initializer. The
scrollView.frame
is the tall blue box, the
EmptySetView
components are the shorter blue boxes.

enter image description here

Despite AutoLayout constraints being set on the xib, my
EmptySetView
isn't filling out the frame I specified for it in the initializer. The console logs show the
scrollView.frame
being the same as the rect for
EmptySetView
, but
EmptySetView
isn't getting drawn properly.

Update 2

The
EmptySetView
xib and
MyViewController
don't know about each other until the xib is loaded. I need to add constraints manually when I load the xib. I think I need to do this in the
EmptySetView
UIView class. Once I get it working I'll post the final solution.

Answer

My guess is that in the nib you are loading the top level view is itself an EmptySetView. This is causing a recursion. You start by instantiating the EmptySetView in code:

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

In setup(), you load the nib. But this causes an EmptySetView to be instantiated again, from the nib. This time, the other initializer is called:

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

But setup() loads the nib, so we are now going around in circles, trying to nest an infinite number of nib-loaded views inside one another like matrushka dolls.