I'm trying to push a view controller using:
var vc2 = ViewController2()
self.navigationController?.pushViewController(vc2, animated: false)
but in my second view controller, I'm have:
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
But I get the error Missing argument for parameter 'coder' in call in the first ViewController. What goes in the parenthesis in the first view controller?
There are two ways you can fix this issue:
The easy way, just call the function passing nil to the parameters:
var vc2 = ViewController2(nibName: nil, bundle: nil)
The best way, create convenience initializers in your class:
class ViewController2: UIViewController {
required init(coder aDecoder: NSCoder) {
fatalError("This class does not support NSCoding")
}
override init (frame : CGRect) {
super.init(frame : frame)
}
convenience override init () {
self.init(frame:CGRectZero)
}
}
and now you can call:
var vc2 = ViewController2()
class ViewController2: UIViewController {
convenience init () {
self.init(nibName: nil, bundle: nil)
}
}
Now you can call ViewController2()
Related
I am using MVVM and want to assign my viewModel to the viewController on the Controllers init. I thought I would achieve this like so:
class LoginViewController: UIViewController, UITextFieldDelegate {
init(loginViewModel: LoginViewModel) {
self.loginViewModel = loginViewModel
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
fatalError("init(coder:) has not been implemented")
}
However I get the error:
Super.init isn't called on all paths before returning from initializer
Is this not the correct route to take? also how would I init the viewModel correctly when it requires an object to do so, but it has to perform a network request first? Init with a blank object instance?
Thanks
edit: this is what im trying now
initWithViewAndViewModel:(loginView: LoginView, loginViewModel: LoginViewModel) {
super.init()
self.loginView = loginView
self.loginViewModel = loginViewModel
}
As a best practice,
Step 1:
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
Step 2:
init(loginViewModel : LoginViewModel) {
super.init(nibName: nil, bundle: nil)
initWithModel:(loginViewModel: LoginViewModel)
}
Step 3: implement initWithModel function
Try adding this if it works:
convenience init() {
self.init(loginViewModel: nil)
}
init(loginViewModel: LoginViewModel?) {
self.loginViewModel = loginViewModel
super.init(nibName: nil, bundle: nil)
}
Please go through this link it might help: https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Initialization.html#//apple_ref/doc/uid/TP40014097-CH18-XID_324
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)
}
Several classes in my app do the same thing and have the same instance variables:
// one of the many classes I have
// they all load nibs and update the frames of their views
class HelpView: UIView {
#IBOutlet var view: UIView!
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)!
NSBundle.mainBundle().loadNibNamed("HelpView", owner: self, options: nil)
self.addSubview(self.view)
self.view.frame = self.bounds
}
}
I want to avoid duplicated code, so I thought about using a superclass so that all the classes inherit from it.
// my new superclass all classes will inherit from
class ReusableView: UIView {
#IBOutlet var view: UIView! // all subclasses have different views
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)!
// every class has a different nib name
NSBundle.mainBundle().loadNibNamed("Nib name goes here", owner: self, options: nil)
self.addSubview(self.view)
self.view.frame = self.bounds
}
}
The problem is that view is nil until the nib is loaded, so it's apparently not possible to call that superclass' method because you're passing a nil object. How can I handle this?
This will work:
class ReusableView: UIView {
func getHelperView() -> UIView! {
preconditionFailure("This method must be overridden")
}
func getNibName() -> String {
preconditionFailure("This method must be overridden")
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)!
// every class has a different nib name
NSBundle.mainBundle().loadNibNamed(self.getNibName(), owner: self, options: nil)
self.addSubview(self.getHelperView())
self.getHelperView().frame = self.bounds
}
}
class HelpView: ReusableView {
#IBOutlet var view: UIView!
override func getHelperView() -> UIView! {
return view;
}
override func getNibName() -> String {
return "NibName";
}
}
All,
I have a bar button item on my ViewController. I have set a computed property to will turn the BarButton off. I want to be able to set this from another class.
Here is my code in the view controller :
class ViewController: UIViewController {
var PayButton : Int {
didSet {
navigationItem.leftBarButtonItem = nil
}
}
required init(coder aDecoder: NSCoder)
{
self.PayButton = 0
super.init(coder: aDecoder)
}
When it try and create an instance on the view controller (so I can set the PayButton integer)
let test = ViewController()
I get an error saying
Missing Argument for parameter 'coder' in call
Any ideas ?
It is asking for the parameter 'coder', because you have it in the required init.
To use your code as it stands, you would need to initialise with:
let test = ViewController(coder: NSCoder)
There are several ways to get around this. The easiest would be to remove the required initialiser.
import UIKit
class ViewController: UIViewController {
var PayButton : Int = 0 {
didSet {
navigationItem.leftBarButtonItem = nil
}
}
}
and then implement with
let test = ViewController()
test.PayButton = 0
Because you have implemented the required initializer in ViewController class.
There are two solutions
Add a default initializer
init() {
super.init(nibName: nil, bundle:nil)
}
Remove the required initializer.
class ViewController: UIViewController {
var PayButton : Int {
didSet {
navigationItem.leftBarButtonItem = nil
}
}
init() {
self.PayButton = 0
super.init(nibName: nil, bundle:nil)
}
required init(coder aDecoder: NSCoder)
{
self.PayButton = 0
super.init(coder: aDecoder)
}
Try this:
This is the required initializer:
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
This is the super initializer:
override init(frame: CGRect) {
super.init(frame: frame)
}
This is your convenience initializer where you can pass the size the view you want to create
convenience init(view: UIView){
self.init(frame: view.frame)
}
This is your convenience initializer where the view is initialized with a value pre defined:
convenience init(){
self.init(frame: CGRectZero) //Put you predefined value here
}
I am trying to create a Class like this :
class MyClass<T:UIView>: UIViewController{
override init()
{
super.init(nibName: nil, bundle: nil);
}
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func loadView() {
self.view = T();
println("loadView")
}
override func viewDidLoad() {
super.viewDidLoad();
println("viewDidLoad")
}
}
When I want to use my class like that :
self.navigationController?.pushViewController(MyClass<UIView>(), animated: true)
The methods viewDidLoad and loadView are never called !!!
Do you know why and if there is some way of doing what I want.
Thanks in advance.
As mentioned in OP comments, Generic class cannot be properly represented in Objective-C.
The workaround would be using the class as a property. something like this:
class MyClass: UIViewController{
let viewCls:UIView.Type
init(viewCls:UIView.Type = UIView.self) {
self.viewCls = viewCls
super.init(nibName: nil, bundle: nil);
}
required init(coder aDecoder: NSCoder) {
self.viewCls = UIView.self
super.init(coder: aDecoder)
}
override func loadView() {
self.view = viewCls();
println("loadView")
}
override func viewDidLoad() {
super.viewDidLoad();
println("viewDidLoad")
}
}
// and then
self.navigationController?.pushViewController(MyClass(viewCls: UIView.self), animated: true)