I've defined a base class with an outlet and attached the outlet to the view in the nib file
class BaseController: UIViewController {
#IBOutlet weak var myView : UIView!
and then created a subclass
class SubViewController: BaseController {
override func viewDidLoad() {
myView.backgroundColor = UIColor.red //The app crashes here
When i call BaseController() it view appears, but when I call SubViewController() the app crashes because myView is nil. The files owner on the nib file is BaseController.
Try to create custom initializer in your subclass:
init() {
super.init(nibName: "BaseController", bundle: nil)
}
Related
Evening I have a problem with outlets.
viewController1 make several instances of ViewController2, presenting them into a page container controlled with a pageControl.
The problem is that the view controller outlets in the ViewController2 are always nil.
Probably because the ViewController2 is instantiate via code.
How can I fix this?
here I create the different ViewController2
let page = OnboardPageViewController(onboard: onboard)
pages.append(page)
Here is the init code for ViewController2
//--------------------
//MARK: - Outlets
//--------------------
#IBOutlet var backgroundVideoView: BackgroundVideo!
#IBOutlet var backgroundUIImage: UIImageView!
#IBOutlet var titleLabel: UILabel!
#IBOutlet var descriptionLabel: UILabel!
//--------------------
//MARK: - Properties
//--------------------
let onboard: Onboard
//--------------------
//MARK: - View's Methods
//--------------------
override func viewDidLoad() {
super.viewDidLoad()
print("loaded: \(onboard.title)")
//FIXME: - need to find a way to link the outlets even if the controller is called via code
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(true)
print("presenting: \(onboard.title)")
}
init(onboard: Onboard) {
self.onboard = onboard
super.init(nibName: nil, bundle: nil)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
You have to instantiate through UIStoryboard object, something like this:
if let viewController = UIStoryboard.init(name: "YourStoryBoardName", bundle: nil).instantiateViewController(withIdentifier: "YourViewController") {
// do something with it
}
You can cast it to your custom class in at the same time of the unwrap (with as? CustomClassViewCotroller)
Edit: static func to instantiate your view controller like init:
class YourViewController: UIViewController {
static func instantiate(withViewModel vm: ViewModel) -> YourViewController? {
if let viewController = UIStoryboard.init(name: "YourStoryboard", bundle: nil).instantiateViewController(withIdentifier: "YourViewController") as? YourViewController {
viewController.viewModel = vm
return viewController
}
return nil
}
var viewModel: ViewModel?
// ...
}
There will be more optional unwrapping in your code when using viewModel var but I think this is the correct way to create view controllers programmatically (in segues you have to set variables too, but that is another history).
Good luck mate.
Your outlets will be nil until the Storyboard file is loaded. So, right after init, they will be nil. You have to wait until viewDidLoad is called before accessing them.
If you need to init and set up things in the VC, you have to add other (non outlet) properties to hold that information. You can't just init and then access an outlet.
EDIT: In your code (added later), you aren't using a XIB or Storyboard. But, since you have outlets, I am assuming that you actually have one.
Don't use a custom init. Instead add properties and set them after you initialize using a Storyboard instantiate.
I am currently trying to set the UImageView of a UIViewController class I defined called MyViewController. In the MyViewController class I have an outlet for the UIImage like so:
#IBOutlet weak var img: UIImageView?
I then try to set it in another class by using:
let vc:MyViewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "test") as! MyViewController
vc.imgView!.image = UIImage(named: "wonders1.png")
The problem is imgView is nil when I force unwrap and I'm not sure why since I'm instantiating the view controller
image of my storyboard:
https://i.stack.imgur.com/ubpQN.png
Error I'm getting:
fatal error: unexpectedly found nil while unwrapping an Optional value
You are trying to add image to an UIImageView instance which isn't initialized yet, the views on UIViewController instance is loaded only after func viewDidLoad() function gets called (read more on iOS life cycle here).
So in order to complete your task you should implement this inside MyViewController:
override func viewDidLoad() {
super.viewDidLoad()
vc.imgView.image = UIImage(named: "wonders1.png")
}
I have one base class
MyViewController: UIViewController
initialized by MyViewController.xib with some outlets. I only have set File Owner class in MyViewController.xib to MyViewController, no any init methods in MyViewController.swift (all inherited from UIViewController), and following line works just as expected:
let vc = MyViewController()
view property is set, outlets is set.
I wish to subclass MyViewController:
SecondViewController: MyViewController
{
override init()
{
super.init()
}
required init?(coder aDecoder: NSCoder)
{
super.init(coder: aDecoder)
}
}
Now I expect that line
let vc = SecondViewController()
will create view controller with view and outlets inherited from MyViewController, but all outlets in vc are nil. Looks like MyViewController.xib file is now missed. What am I doing wrong?
You can't extend the xib file. The SecondViewControllershould have its own xib file and its own outlets. You may define the common UI components in the base class MyViewController and for each xib you create, link the ui components directly to the base class.
For example, if you have a common custom back button in all view controller, add the outlet definition in the base class and for each xib file add the UIButton and set its outlet to the base class.
I am trying to initialise a subclass of UIViewController called TestController. I have this swift class:
class TestController : UIViewController {
let testString : String
#IBOutlet weak var test: UITextField!
required init(withString string: String) {
self.testString = string
super.init(nibName: nil, bundle: nil)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
I also have a nib file called TestController,
inside it is an instance of UIViewController. The file's owner is none (NSObject in grey) and the class of the UIViewController instance in designer is set in the identity inspector to TestController. There is a UITextField instance as outlet.
The problem is the controller is not initialised from the nib (I think), and the textField outlet is nil.
My goal is to allow initialization of controller's instance from nib programmatically, via the custom initialiser. What am I doing wrong?
You have two problems: First, your nib (or xib) is not created properly (you shouldn't see TestController in interface builder). The easiest way to fix this is to recreate it: New File->Cocoa Touch Class->Create Subclass of UIViewController, and don't forget to check Also Create a XIB file
The second problem is that you're not specifying what nib you want to load. You should specify it in nibName parameter of the initializer:
super.init(nibName: "NewlyCreatedXib", bundle: nil)
When you fix both issues, I believe your code will work properly.
This question already has an answer here:
IBOutlet of another view controller is nil
(1 answer)
Closed 7 years ago.
I customized a viewcontroller with xib, and this viewcontroller has a label.
I have already connected the label with xib.
But when I use this label, it is nil.
What's wrong I did?
below is my code.
import UIKit
class MyViewController: UIViewController {
#IBOutlet weak var label: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
code to use this viewcontroller
let myViewController = MyViewController(nibName: "MyViewController", bundle: nil)
myViewController.label.text = "test"
presentViewController(myViewController, animated: true) { () -> Void in
println("ok")
}
Thank you.
The issue is you are trying to access the label before it is loaded.
The following changes may solve your problem;
import UIKit
class MyViewController: UIViewController {
var labelText: String!
#IBOutlet weak var label: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
//Setting Label text here
label.text = labelText;
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
and
let myViewController = MyViewController(nibName: "MyViewController", bundle: nil)
myViewController.labelText = "test"
presentViewController(myViewController, animated: true) { () -> Void in
println("ok")
}
From the documentation:
The nib file you specify is not loaded right away. It is loaded the first time the view controller's view is accessed. If you want to perform additional initialization after the nib file is loaded, override the viewDidLoad method and perform your tasks there.
You may access the label from viewDidLoad. If you need to access the label externally, make sure you have accessed the view property first to force the view to be loaded.
The view of the view controller is not loaded. You can either use the viewDidLoad() method inside your view controller or you load it by accessing the view property. It is lazy loaded and will load your view.
So before accessing viewController.view.label write print(viewController.view) to lazy load the view