I have custom Subclass of UIView with an associated .xib file. In the .swift file of my class inside the required init?(_:):
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
let xibView = loadViewFromNib(named: "RangeSelectorView")
xibView.frame = self.bounds
xibView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
self.addSubview(xibView)
}
I'm calling loadViewFromNib(_:), which does the following:
func loadViewFromNib(named name: String) -> UIView {
let bundle = Bundle(for: type(of: self))
let nib = UINib(nibName: name, bundle: bundle)
let view = nib.instantiate(withOwner: self, options: nil)[0] as! UIView
return view
}
Inside of a prototype cell of a TableViewController in my Storyboard, I have a UIView, which shall be an instance of my custom class. The code does compile without any problems but as soon as the TableViewController should appear on screen, it crashes without giving me any explicit error message.
Any ideas what i'm doing wrong?
--Update:--
If I comment out the required init?(_:) and the only outlet of my .swift file (the view itself) of my custom view everything works fine so far. But then I have the problem that I can't connect Actions to repeating content, which is the case when being inside the content view of a prototype cell. This was the reason why I'm trying to source out the class to a .xib file because it will have two different gesture recognizers and some Actions triggered by them.
Related
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
guard let view = loadViewFromNib() else { return }
view.frame = self.bounds
self.addSubview(view) // My question
}
func loadViewFromNib() -> UIView? {
let bundle = Bundle(for: type(of: self))
let nib = UINib(nibName: self.className, bundle: bundle)
return nib.instantiate(withOwner: self, options: nil).first as? UIView
}
i want customXibView initial with nib .look like self = loadViewFromNib(), not by self.addSubview(nibView) .
I found that many of the answers are all done self.addSubview(nibView). In fact, look our view hierarchy,we will found a extra view.
This is my view hierarchy
Is there any way to instance xib to storyboard, not by self.addSubview(nibView).
if you don't understand my question,look this Custom UIView from Xib - requirements, good practices. I hope some one help me.
There are three scenarios:
You have CustomView class with all of its UI defined
programmatically in its drawRect method.
You have CustomView class with its UI defined in a subview of storyboard scene with its class set to 'CustomView'.
You have CustomView class with its UI defined in a separate xib.
Now if you want to include your CustomView in as a subview in your viewcontroller, the method differs depending how your class is defined in the first instance. Secondly how do you want to include it in you viewcontroller.
Adding CustomView with scenario 1:
Initialise the CustomView instance in your viewDidLoad method and add it to your viewcontroller's view as a subview.
Adding CustomView with condition 2:
You don't have to do anything in this scenario. Storyboards will initizlize your class for you. You can do additional setup in init(coder) method that will be called when you view is initialized.
Adding CustomView with condition 3:
The proper way to initialise a view with an xib is to initialise it programmatically via UINib's inistantiateWithOwner method. Then add the view as a subview in the viewDidLoad method of your viewcontroller.
What you are doing (or what I can guess seeing your image hierarchy) is that you have changed the class of your UIView to your custom class. Now since you have a separate xib, you are initialising it in init(coder) method and adding it as a subview to the view that was defined in and initialised by the storyboard. Hence you end up with an extra container view.
If you don't want the extra view, add your custom view programmatically.
OR
(If possible) copy the view defined in xib and place it in the viewcontroller dock and then link the view with an outlet and add it in viewDidLoad method.
Here's the answer you've wanted all along. You can just create your CustomView class, have the master instance of it in a xib with all the subviews and outlets. Then you can apply that class to any instances in your storyboards or other xibs.
No need to fiddle with File's Owner, or connect outlets to a proxy or modify the xib in a peculiar way, or add an instance of your custom view as a subview of itself (i.e. no need for self.addSubview(view), which is your question).
Just do this:
Import BFWControls framework
Change your superclass from UIView to NibView (or from UITableViewCell to NibTableViewCell)
That's it!
It even works with IBDesignable to refer your custom view (including the subviews from the xib) at design time in the storyboard.
You can read more about it here:
https://medium.com/build-an-app-like-lego/embed-a-xib-in-a-storyboard-953edf274155
And you can get the open source BFWControls framework here:
https://github.com/BareFeetWare/BFWControls
And here's a simple extract of the NibReplaceable code that drives it, in case you're curious:
https://gist.github.com/barefeettom/f48f6569100415e0ef1fd530ca39f5b4
Tom 👣
Its not necessary that you have to add the xib view as subview. Think about the usage, as per my understanding there are tow scenarios
Suppose you have a view called customXibView class and its xib, now in the xib select the view and and from the right side panel select [identity inspector] option and check, under [custom class] by default its class is UIView. So you can keep it as UIView and set the file owner as customXibView and load it from xib as UIView instance as you are doing now. So, now the question is if you allocate customXibView object manually or in some other xib how it will show your custom components which are in your view xib. So in that case in your customXibView's init method you have to load the UIView xib and add it to customXibViewobject's view.
Another case where you can set the xib's view [custom class] as your class customXibView in this case when you call the below method
func loadViewFromNib() -> customXibView? {
let bundle = Bundle(for: type(of: self))
let nib = UINib(nibName: self.className, bundle: bundle)
return nib.instantiate(withOwner: self, options: nil).first as? customXibView
}
You directly get an object of customXibView with UI loaded no need to add it to any other view. But in this case if you allocate the customXibView class object your UI will not be loaded if you don't do what what you are doing now. If any doubt plz comment.
I have a UIViewController with a button that opens a Modal/Popup (XIB)
I use this code for that:
let aView: myView1 = UINib(nibname: "AView", bundle : nil).instantitate(withOwner: self, options: nil)[0] as! myView1
aView.frame = self.view.frame
self.view.addSubView(aView)
The Modal contains a button that should open a Form (another XIB)
I use this code for that:
let bView: myView2 = UINib(nibname: "BView", bundle : nil).instantitate(withOwner: self, options: nil)[0] as! myView2
bView.frame = self.view.frame
self.view.addSubView(bView)
But I get a compile time error, saying:
Value of type 'AView' has no member 'view'
When I changed the code from:
self.view.addSubview(bView)
to:
self.addSubview(bView)
I get a runtime error when using above line in UIView:
this class is not key value coding-compliant for the key view.
If I relocate the above line to UIViewController, I get a different error:
Can't add self as subview
Is there some other way that works to open a UIView from another UIView, without going back or making changes to the UIViewController?
First i suggest change class name from myView1 to MyView1, same to myView2. Then set your nib files file's owner from interface builder to MyView1 and MyView1.
Change this withOwner: self to nil in below call:
let aView:myView1 = UINib(nibname: "AView", bundle : nil).instantitate(withOwner: self, options: nil)[0] as! myView1
OR
You might need to remove file's owner from interface builder.
I am programmatically creating a split view controller using the following code when a table view cell is touched:
let rootViewController: UIViewController = RootTableViewController()
let navVC: UINavigationController = UINavigationController(rootViewController: rootViewController)
let detailViewController: UIViewController = DetailTableViewController()
let splitVC: UISplitViewController = UISplitViewController()
splitVC.viewControllers = [navVC, detailViewController]
self.present(splitVC, animated: true, completion: nil)
but when I tap the tableViewCell I get the error: 'fatal error: unexpectedly found nil while unwrapping an Optional value' which appears to be linked to a UITextField (and all UI Elements) on the RootTableViewController. The first failure is in the viewDidLoad of RootViewController after executing the above code when a value is passed to a ui element
Where exactly does this happen? Wheres the error originated? I had a similar issue where I tried to access IBOutlets before they were created by the system which caused my app to crash.
Specifically I had a UI update function which was called after setting a property of the ViewController.
I got around this by checking for nil in the update function and since it was called before viewDidLoad was called the first time, I called the update function in viewDidLoad manually to make sure that when it shows for the first time, everything is correctly updated.
UPDATE
I think I have an idea of whats going on. You created a Storyboard, setup your UI and chose your ViewControllers as the classes of the ViewControllers in the Storyboard.
If you now want to use the ViewControllers, you have to instantiate them via the Storyboard rather than manually initializing them (something like this):
let storyboard = UIStoryboard(name: "MyStoryboardName", bundle: nil)
let controller = storyboard.instantiateViewController(withIdentifier: "someViewController")
You also could use Segues instead of instantiating something at all. Build your complete UI using the Storyboard and then use a Segue to present the SplitViewController.
The last method I can think of is, that if you want to instantiate the ViewControllers manually and still make use of the Storyboard or a nib, you have to do some custom initialization in in the init functions of your ViewControllers (this code is from a custom view I have in a separate .xib):
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
initializeSubviews()
}
override init(frame: CGRect) {
super.init(frame: frame)
initializeSubviews()
}
func initializeSubviews() {
UINib(nibName: "DatePickerKeyboard", bundle: nil).instantiate(withOwner: self, options: nil)
addSubview(view)
view.frame = self.bounds
}
I'm defining a Custom UIView based on a XIB. Therefor I have copied CustomXIBSwift-master from Github. That works very well. I adapted it briefly for my purposes.
The label (lblTitle) is linked as a referencing outlet to the File's Owner
Now I also want to make a referencing outlet for the second sublabel to File's Owner. However I'm not able to linke this using the dot-line method nor by including the IBOutlet manually.
Based on the CustomXIBSwift-master the XIB is of type UIView. When I specify a custom class (instead of UIView) it crashes.
Part of the SWIFT code:
func loadViewFromNib() {
let bundle = Bundle(for: type(of: self))
let nib = UINib(nibName: "SlidingAlertView", bundle: bundle)
let view = nib.instantiate(withOwner: self, options: nil)[0] as! UIView
view.frame = bounds
view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
self.addSubview(view);
}
How can I make a referencing outlet from this View to the Swift file?
I was not able to make a connection between the UILabels and the File's Owner. It appeared that also for the File's Owner thee Custom Class can be specified.
I've got a UIView designed in InterfaceBuilder that has a Swift backing class. I'm using awakeAfterUsingCoder() to substitute the instantiated nib like so:
public override func awakeAfterUsingCoder(aDecoder: NSCoder) -> AnyObject? {
if self.subviews.count == 1 && self.subviews[0].isKindOfClass(UILabel) {
let bundle = NSBundle(forClass: self.dynamicType)
let nib = UINib(nibName: "CustomView", bundle: bundle).instantiateWithOwner(nil, options: nil)
return nib[0] as! CustomView
}
return self
}
However, now I keep getting EXC_BAD_ACCESS crashes in the AppDelegate. I turned on zombies and was able to get this message:
*** -[ModuleName.CustomView _referenceView]: message sent to deallocated instance 0xed13600
_referenceView must be defined somewhere in the framework, though I didn't see it when I navigated self stopped at a breakpoint. What am I doing wrong?
The view in my storyboard was defined as a UIButton (since my CustomView is a UIButton subclass). I replaced it with a generic empty UIView, I replaced the condition with:
if self.subviews.isEmpty {
and everything worked right.