Required initializers for a subclass of UIViewController - ios

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?

Related

Using a variable from a separate view controller in Swift

I am trying to get input from a text field and display that on a label in a separate view controller.
I am trying to get the partyID from PartyViewController:
class PartyViewController: UIViewController {
// CALLS LOGIN VC
var LoginViewController: LoginViewController?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBOutlet weak var partyID: UITextField!
Into my second view controller:
class GuestPartyViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBOutlet weak var PartyViewController.partyID:
UILabel!
This gives me several errors..
Class 'GuestPartyViewController' has no initializers
Consecutive declarations on a line must be separated by ';'
Expected declaration
Type annotation missing in pattern
You can access variable like this:
class PartyViewController: UIViewController {
var partyID: Type = Value
init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
}
}
Another controller
var GuestPartyViewController: PartyViewController = PartyViewController(nibName: nil, bundle: nil)
var getPartyId = GuestPartyViewController.partyID

about initializing of viewcontroller (swift)

I have a ViewController file called TwoViewController.swift and a nib file called TwoViewController.xib.
TwoViewController.swift like this ↓
class TwoViewController: UIViewController {
var pageTitle: String?
・・・・・・
override func viewDidLoad() {
super.viewDidLoad()
}
・・・・・・
}
then, I would new a TwoViewController and present it at OneViewController.swift like this↓
class OneViewController: UIViewController {
・・・・・・
override func viewDidLoad() {
super.viewDidLoad()
}
・・・・・・
func presentTwo() {
let two = new TwoViewController()
two.pageTitle = "2222"
self.present(two, animated: false, completion: nil)
}
}
But, I want to new TwoViewController and set value to property pageTitle at the same time like this ↓
new TwoViewController(pageTitle: "22222")
To do that, I think I need create an init method at TwoViewController.
I tried to make an init method like below↓. Is this correct?
class TwoViewController: UIViewController {
var pageTitle: String
init(pageTitle: String) {
super.init(nibName: nil, bundle: nil)
self.pageTitle = pageTitle
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func viewDidLoad() {
super.viewDidLoad()
}
・・・・・・
}
You could do so, but then you'd have to initialize pageTitle in every initializer with some default value, which you typically don't know.
Therefore, this is not quite common to do so. Instead assign the property value after initialization, like you did originally (in funcTwo), and go on with processing in viewDidLoad:
class TwoViewController: UIViewController {
var pageTitle: String!
override func viewDidLoad() {
// use pageTitle to fill some outlet or so:
self.title = pageTitle
}
}
or make pageTitle an optional and check in viewDidLoad if it is set.
By the way: If you follow the naming scheme and name your XIB file like your view controller, you could use the implicit form:
let twoController = TwoViewController.init()
or explicitly
let twoController = TwoViewController.init(nibName: "TwoViewController", bundle: nil)
you should initialise your TwoViewController from your nib file like this :
let twoController = TwoViewController.init(nibName: "TwoViewController", bundle: nil)
then you can initialise your pageTitle like this :
twoController.pageTitle = "2222"
then you can present your twoViewController like this :
self.present(twoController, animated: false, completion: nil)

Has no accessible initializers (UITableView) [duplicate]

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

Variable not changed in custom initialiser

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?

ios Swift fatal error: use of unimplemented initializer 'init()'

I've been trying very hard, have looked up every similar question pertaining to this issue on StackOverflow and trying them to no avail.
class TimeLineTableViewController:
UITableViewController,
UIImagePickerControllerDelegate,
UINavigationControllerDelegate {
var timelineData = [PFObject]()
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func viewDidLoad() {
super.viewDidLoad()
self.loadData()
}
#IBAction func loadData(){
timelineData.removeAll(keepCapacity: false)
var findTimelineData:PFQuery = PFQuery(className:"timelineMessages")
findTimelineData.findObjectsInBackgroundWithBlock { (objects:[AnyObject]! , error:NSError!) -> Void in
if error == nil {
self.timelineData = objects.reverse() as [PFObject]
//let array:NSArray = self.timelineData.reverseObjectEnumerator().allObjects
// self.timelineData = array as NSMutableArray
self.tableView.reloadData()
}
}
}
override func viewDidAppear(animated: Bool) {
var footerView:UIView = UIView(frame: CGRectMake(0, 0, self.view.frame.size.width, 50))
self.tableView.tableFooterView = footerView
var logoutButton:UIButton = UIButton.buttonWithType(UIButtonType.System) as UIButton
logoutButton.frame = CGRectMake(20, 10, 50, 20)
logoutButton.setTitle("Logout", forState: UIControlState.Normal)
logoutButton.addTarget(self, action:"logout:", forControlEvents: UIControlEvents.TouchUpInside)
footerView.addSubview(logoutButton)
}
To clarify, timelineTableViewController has one class that inherits, MessageTableCell. It's also part of a project that I've integrated into Objective-C code, so it's a combination of both Swift and ObjC. I've run both projects (the swift one and the ObjC one) independently and they work fine; it's only when I try to run it together do they mess up. Any suggestions? I'm at an utter loss for this.
“Unlike subclasses in Objective-C, Swift subclasses do not inherit their superclass initializers by default.”
Automatic Initializer Inheritance
Rule 1:
If your subclass doesn’t define any designated initializers, it automatically inherits all of its superclass designated initializers.
Rule 2:
If your subclass provides an implementation of all of its superclass designated initializers—either by inheriting them as per rule 1, or by providing a custom implementation as part of its definition—then it automatically inherits all of the superclass convenience initializers.
Excerpt From: Apple Inc. “The Swift Programming Language.” iBooks. https://itun.es/tw/jEUH0.l
Since you have override the init(coder aDecoder: NSCoder), TimeLineTableViewController won't have the init() initiailzer.
You can provide an implementation of all of its superclass designated initialisers like this
override init() {
super.init()
}
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
, or just delete the implementation of init(coder aDecoder: NSCoder).

Resources