Can't Load Uiviewcontroller Xib File in Storyboard in Swift

Can't Load UIViewController XIB file in Storyboard in Swift

What's happened is that Seed 5 broke the mechanism whereby a view controller can find its xib by name, if the view controller is a Swift class. The reason is that the name of the class, in Swift's mind, is not the same as the name you gave it (and the name you gave the xib file); the name has been "mangled", in particular by prepending the module name (i.e. Swift classes have namespacing).

I offer three workarounds:

  • Your workaround is a good one (load the .xib file by name explicitly)

  • Name the .xib file MyModule.TestViewController.xib, where MyModule is the name of your bundle (i.e. the name of the project) (this is what Apple advises, but I hate it)

  • Use @objc(TestViewController) before the view controller's class declaration to overcome the name mangling which is what's breaking the mechanism (this is the approach I favor)

See my discussion here: https://stackoverflow.com/a/25163757/341994 and my further discussion linked to from there: https://stackoverflow.com/a/25152545/341994

EDIT This bug is fixed in iOS 9 beta 4. If the nib file search fails, iOS 9 now strips the module name off the view controller class name and performs the nib file search a second time.

UIViewController defined in a xib file doesn't load in the storyboard

Am I mistaken to expect the contents of the NibViewController to appear in the storyboard?

Yes, you are mistaken. Your app is working perfectly so you should stop worrying and just proceed.

By deleting the view from the view controller in the storyboard, you have specifically instructed the storyboard: "Do not make a view for this view controller. At runtime, the view should come from the xib file, not from you."

And that is exactly what does happen at runtime. So just design your interface in the xib file and all will be well.

Load view from an external xib file in storyboard

My full example is here, but I will provide a summary below.

Layout

Add a .swift and .xib file each with the same name to your project. The .xib file contains your custom view layout (using auto layout constraints preferably).

Make the swift file the xib file's owner.

enter image description here
Code

Add the following code to the .swift file and hook up the outlets and actions from the .xib file.

import UIKit
class ResuableCustomView: UIView {

let nibName = "ReusableCustomView"
var contentView: UIView?

@IBOutlet weak var label: UILabel!
@IBAction func buttonTap(_ sender: UIButton) {
label.text = "Hi"
}

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

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
}
}

Use it

Use your custom view anywhere in your storyboard. Just add a UIView and set the class name to your custom class name.

enter image description here

How to Load a viewController from a Xib file using segue? Swift 5

performSegue is one of UIViewController's methods, so it doesn't work on UICollectionViewCell. Instead, you'll need to call performSegue from the parent view controller that contains the collection view.

You can use delegates or closures for this, but I prefer closures. First, add one inside PetNameInfoCollectionViewCell:

class PetNameInfoCollectionViewCell: UICollectionViewCell {
var photoTapped: (() -> Void)? /// here!

@IBAction func takeAPhoto(_ sender: UIButton) {
photoTapped?() /// call it
}
}

Then, assign the closure inside the parent view controller's cellForItemAt.

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if indexPath.item == 0 {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: userNameInfoCollectionViewCellId, for: indexPath) as! userNameInfoCollectionViewCell /// replace with the cell class
cell.photoTapped = { [weak self] in
self?.performSegue(withIdentifier: "UIImagePickerSegue", sender: nil)
}
return cell

} else {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: PetNameInfoCollectionViewCell, for: indexPath) as! PetNameInfoCollectionViewCell /// replace with the cell class
cell.photoTapped = { [weak self] in
self?.performSegue(withIdentifier: "UIImagePickerSegue", sender: nil)
}
return cell
}
}

Be able to load xib from both Storyboard and ViewController

To preserve both cases, I preferred to write this inside my subclass declaration:

@IBDesignable class CustomView: UIView {

var view: UIView!

@IBOutlet weak var button: UIButton!
@IBOutlet weak var label: UILabel!

func xibSetup() {
view = loadViewFromNib()
view.frame = bounds
view.autoresizingMask = [UIViewAutoresizing.flexibleWidth, UIViewAutoresizing.flexibleHeight]
addSubview(view)
}

func loadViewFromNib() -> UIView {

let bundle = Bundle(for: type(of: self))
let nib = UINib(nibName: "CustomView", bundle: bundle)
let view = nib.instantiate(withOwner: self, options: nil)[0] as! UIView

return view
}

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

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

In this way I can see and add it inside my storyboard.
In ViewForHeaderInSection method, I wrote this:

func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {

let header = UIView()

let view:CustomView = CustomView(frame: CGRect(x: 0, y: 0, width: self.view.frame.width, height: 30))

view.label.text = "header title: \(section)"

header.addSubview(view)

return header

}

And so it works ;)

Instantiating a UIViewController directly from a xib file which is better?

That's an opinion based question (or rather asking for an opinion as answer), but I'll let it slide. :)

The reason why I'd say the first method is "bad practice" or obscure, as Phillip Mills correctly said) is that you're basically making assumptions on the xib and potentially load more than you must. The second method is suited for a xib that's specifically belonging to your ViewController class (if the name fits, you don't even have to specify that, at least in the Objective-C equivalent). Hence the xib is a parameter of the initializer.

The first method takes a longer route. It loads a nib (and that might include any other objects in that, too). Then you assume that the first object on its top level is the view controller you want. One accidental drag in your storyboard and you'll be having fun raising your eyebrows for unexpected behavior. Also you ommit the owner parameter, which might or might not have consequences depending on what you do.

In general the first method is using a method that's more meant to be used for xibs that are containers for multiple objects (not necessarily view controllers), while the second one is the usual way to go (or "best practice") when loading a view controller with its associated xib file.

You can go the first route, but that's basically hiking on socks, imo. It works, but people will look at you in a weird way...

Swift - how to load a xib file from a view controller in the story board?

Solved it...it was right in front of my eyes, guess i had to ask the question to write the answer myself, haha, I'm so smart, i added this code to the view controller from the story board and it works like a charm.

import UIKit

class ViewController: UIViewController, communicationControllerM {

@IBOutlet weak var Btn: UIButton!

override func viewDidLoad() {
super.viewDidLoad()

Btn.addTarget(self, action: #selector(callMenuModal), forControlEvents: .TouchUpInside)
}

func callMenuModal() {
let mainVC = MainViewController()
mainVC.delegate = self
mainVC.modalPresentationStyle = .OverCurrentContext
presentViewController(mainVC, animated: false, completion: nil)
}

override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}

func backFromM() {
self.dismissViewControllerAnimated(true, completion: nil)
}
}

in my xib view controller i added a return action

playBtn.addTarget(self, action: #selector(goBackToMainVC), forControlEvents: .TouchUpInside)
self.delegate?.backFromM()

Thanks guys!!

Framework iOS-XIB can't load NIB in package

I found a solution to this problem,
push the xib file to the resource folder

spec.resources     = "MobileAds/**/*.{png,jpeg,jpg,storyboard,xib,xcassets}"


Related Topics



Leave a reply



Submit