I am trying to set a value of variable in swift when initialising an instance of a UINavigationController subclass. The code looks like this:
private var initWithRootViewController = false
init() {
super.init(rootViewController: contentVC)
initWithRootViewController = true
}
init(parameters: OAuth2Parameters) {
initWithRootViewController = true
super.init(rootViewController: contentVC)
initWithRootViewController = true
//defer { initWithRootViewController = true }
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
//initWithRootViewController = true
}
override func viewDidLoad() {
super.viewDidLoad()
if !initWithRootViewController {
setViewControllers([contentVC], animated: false)
}
}
The problem is that in my custom initialiser (init(parameters: OAuth2Parameters)) the value of initWithRootViewController is never changed. I have even tried to set it before and after super.init is called as shown in the code and to set it after initialisation (commented defer line). Neither works. I have also tried to clean (and clean build folder) in Xcode and reset the simulator.
I inspected the value of initWithRootViewController by setting breakpoints. What I discovered was that in the custom initialiser the property changes value to true, but as soon as it goes out of the scope of the initialiser (when init(nibName...) is called), the value says false and it stays like that when inspected in If I change the value in init(nibName...) then it is set properly. Why can I not change the value of the variable in my custom init method?
Related
I am trying to assign default value for instead of fetching Nul. But I am getting an error:
Must call a designated initializer of the superclass 'UIViewController'
What should I do to eliminate the error?
Code:
var destination : CLLocationCoordinate2D
init(){
super.init()
self.destination.latitude = Double("")!
self.destination.longitude = Double("")!
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
The error suggests that this class is a UIViewController. The designated initializer for a UIViewController requires nibName & bundle. So when you call super.init without the arguments, it throws an error since the init method for UIViewController (which is the super class in this case) requires those arguments.
Basically, you have to replace super.init() with
super.init(nibName: String?, bundle: Bundle?)
A better approach:
Override the init method
var destination : CLLocationCoordinate2D
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
self.destination.latitude = Double("")!
self.destination.longitude = Double("")!
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
The right approach:
Don't use init at all. Instead, use UIViewController's lifecycle methods like viewDidLoad, viewWillAppear, & viewDidAppear
Apologies if this has been asked before, I've searched around a lot and many answers are from earlier Swift betas when things were different. I can't seem to find a definitive answer.
I want to subclass UIViewController and have a custom initializer to allow me to set it up in code easily. I'm having trouble doing this in Swift.
I want an init() function that I can use to pass a specific NSURL I'll then use with the view controller. In my mind it looks something like init(withImageURL: NSURL). If I add that function it then asks me to add the init(coder: NSCoder) function.
I believe this is because it's marked in the superclass with the required keyword? So I have to do it in the subclass? I add it:
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
Now what? Is my special initializer considered a convenience one? A designated one? Do I call a super initializer? An initializer from the same class?
How do I add my special initializer onto a UIViewController subclass?
class ViewController: UIViewController {
var imageURL: NSURL?
// this is a convenient way to create this view controller without a imageURL
convenience init() {
self.init(imageURL: nil)
}
init(imageURL: NSURL?) {
self.imageURL = imageURL
super.init(nibName: nil, bundle: nil)
}
// if this view controller is loaded from a storyboard, imageURL will be nil
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
}
For those who write UI in code
class Your_ViewController : UIViewController {
let your_property : String
init(your_property: String) {
self.your_property = your_property
super.init(nibName: nil, bundle: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) is not supported")
}
}
This is very similar to the other answers, but with some explanation. The accepted answer is misleading because its property is optional and doesn't expose the fact that your init?(coder: NSCoder) MUST initialize each and every property and the only solution to that is having a fatalError(). Ultimately you could get away by making your properties optionals, but that doesn't truly answer the OP’s question.
// Think more of a OnlyNibOrProgrammatic_NOTStoryboardViewController
class ViewController: UIViewController {
let name: String
override func viewDidLoad() {
super.viewDidLoad()
}
// I don't have a nib. It's all through my code.
init(name: String) {
self.name = name
super.init(nibName: nil, bundle: nil)
}
// I have a nib. I'd like to use my nib and also initialze the `name` property
init(name: String, nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle? ) {
self.name = name
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
}
// when you do storyboard.instantiateViewController(withIdentifier: "ViewController")
// The SYSTEM will never call this!
// it wants to call the required initializer!
init?(name: String, coder aDecoder: NSCoder) {
self.name = "name"
super.init(coder: aDecoder)
}
// when you do storyboard.instantiateViewController(withIdentifier: "ViewController")
// The SYSTEM WILL call this!
// because this is its required initializer!
// but what are you going to do for your `name` property?!
// are you just going to do `self.name = "default Name" just to make it compile?!
// Since you can't do anything then it's just best to leave it as `fatalError()`
required init?(coder aDecoder: NSCoder) {
fatalError("I WILL NEVER instantiate through storyboard! It's impossible to initialize super.init?(coder aDecoder: NSCoder) with any other parameter")
}
}
You basically have to ABANDON loading it from storyboard. Why?
Because when you call a viewController storyboard.instantiateViewController(withIdentifier: "viewController") then UIKit will do its thing and call
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
You can never redirect that call to another init method.
Docs on instantiateViewController(withIdentifier:):
Use this method to create a view controller object to present
programmatically. Each time you call this method, it creates a new
instance of the view controller using the init(coder:) method.
Yet for programmatically created viewController or nib created viewControllers you can redirect that call as shown above.
Convenience initializers are secondary, supporting initializers for a
class. You can define a convenience initializer to call a designated
initializer from the same class as the convenience initializer with
some of the designated initializer’s parameters set to default values.
You can also define a convenience initializer to create an instance of
that class for a specific use case or input value type.
They are documented here.
If you need a custom init for a popover for example you can use the following approach:
Create a custom init that uses the super init with nibName and bundle and after that access the view property to force the load of the view hierarchy.
Then in the viewDidLoad function you can configure the views with the parameters passed in the initialization.
import UIKit
struct Player {
let name: String
let age: Int
}
class VC: UIViewController {
#IBOutlet weak var playerName: UILabel!
let player: Player
init(player: Player) {
self.player = player
super.init(nibName: "VC", bundle: Bundle.main)
if let view = view, view.isHidden {}
}
override func viewDidLoad() {
super.viewDidLoad()
configure()
}
func configure() {
playerName.text = player.name + "\(player.age)"
}
}
func showPlayerVC() {
let foo = Player(name: "bar", age: 666)
let vc = VC(player: foo)
present(vc, animated: true, completion: nil)
}
Take a look at the code below to see what I am inquiring about.
class EmploymentTableViewController: UITableViewController {
...
// MARK: - Initialization
override init() {
super.init()
self.title = "Employment"
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.title = "Employment"
}
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
self.title = "Employment"
}
override init(style: UITableViewStyle) {
super.init(style: style)
self.title = "Employment"
}
...
}
I find myself reimplementing this a lot in my view controllers. If I override just one initializer function, I must in turn reimplement it in all possible initialization functions else the program will crash.
Are there any courses of action I can take to ensure that {class}.title is set at initialization without needing to make such verbose code?
The end result is to have the title make it's way to the tab bar item it will be linked to. When adding this to a tab bar controller, the title of the tab bar item is automatically set to the title of the view controller.
Thank you,
Steven
I have created file called MyHelper.swift
and I created class inside it:
public class MyHelper{
//..... variables here
public init(list listOfViews: [UIView]){
self.listOfViews = listOfViews
self.time = 1.0;
}
}
then i declared an object in UIViewController like this
class ViewController: UIViewController {
var myHelper: MyHelper;
override func viewDidAppear(animated: Bool) {
myHelper = MyHelper(listOfViewsToAnimatin: listOfViews)
}
// ..... rest of the code
}
but i got error that says:
**
Class "ViewController" has no initializers.
**
I tried the default fixes suggested in xcode:
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
It caused another error.
then i tried this code from internet:
required init(coder aDecoder: NSCoder!)
{
super.init(coder: aDecoder)
}
It says :
Property self.myHelper not initialized at super.init call
How on earth i can use object of MyHelper class inside UIViewController !?
This is Swift's compile time checking at work.
You'll need to either setup the MyHelper in the init method, or mark it as optional (note the question mark at the end of the var declaration):
class ViewController: UIViewController {
var myHelper: MyHelper?
required init(coder aDecoder: NSCoder!)
{
super.init(coder: aDecoder)
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
myHelper = MyHelper(listOfViewsToAnimatin: listOfViews)
}
// ..... rest of the code
}
You are initializing the MyHelper in viewDidAppear. It needs to be initialized in init (before super.init()) or you need to declare it as optional and set to nil.
var myHelper: MyHelper? = nil
You can make myHelper optional:
var myHelper:MyHelper?
When you use it, unwrap it first with:
if let myHelper = myHelper {
myHelper.yourFunction()
} else {
// self.myHelper == nil
}
Alternatively you can unwrap with !:
myHelper!.yourFunction()
But it will crash if myHelper is nil.
I've been attempting to follow a tutorial about creating a container view controller. It's in Objective-C. I want to convert it to Swift. I've found some of the same questions here, but I didn't get too much out of them.
Here's the code.
import UIKit
class ContainerViewController: UIViewController { // Class "ContainerViewController" has no initializers - That I know why.
// 'required' initializer 'init(coder:)' must be provided by a subclass of UIViewController
var currentDetailViewController: UIViewController
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
I've tried doing what both errors say, but still doesn't work.
The problem is: If you declare any stored properties without initial value, you must implement your own initializer to initialize them. see this document.
Like this:
var currentDetailViewController: UIViewController
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) {
currentDetailViewController = UIViewController()
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
}
convenience override init() {
self.init(nibName: nil, bundle: nil)
}
required init(coder aDecoder: NSCoder) {
currentDetailViewController = UIViewController()
super.init(coder:aDecoder)
}
But, I think this is not what you want.
The correct solution depends on where you initialize currentDetailViewController.
If you always initialize it within viewDidLoad, then you can declare it as an "Implicitly Unwrapped Optional"
var currentDetailViewController: UIViewController!
override viewDidLoad() {
super.viewDidLoad()
self.currentDetailViewController = DetailViewController()
}
otherwise, If currentDetailViewController can be nil, you should declare it as an "Optional"
var currentDetailViewController: UIViewController?