Accessing function in MasterViewController from DetailViewController (swift) - swift

I have a function in MasterViewController
func removeLocation(city: String){
objects.removeObject(city)
self.tableView.reloadData()
}
In my DetailViewController I check whether the city is valid and if its not, I want to remove it from the table in MasterViewController. I pass self in prepareForSegue() from MasterView to DetailView and I assign it to
var masterViewController: MasterViewController
But then I get an error saying that it is not initialized and it want me to have this initializer
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
Which breaks my whole program when I run it and gives me an expected fatal error.
Ho I can create an instance of MasterView in DetailView without an initializer, or access a function in MasterView from DetailView?
This is Swift btw, I found some advices on how to solve it in C but I couldn't implement them.

Use an optional.
var masterViewController: MasterViewController?
Optionals do not have to be initialized when the class is created.
In prepareForSegue() assign the pointer as usual:
destinationViewController.masterViewController = self
Then when you need to call removeLocation:
masterViewController?.removeLocation("London")

Related

How do you initialize a View Controller that is created completely programmatically and which requires a coder initializer [duplicate]

I am trying to initialise a UIViewController in my Swift application but I am facing a problem to which I cannot find any definitive answer.
I would like to call this from a FlowCoordinator to initialise the controller, but my initialiser requires a NSCoder object due to the required init?(coder: NSCoder) function.
MyAwesomeController()
Is there a way to initialise differently the controller, without the need to pass the NSCoder object?
If there is not, how can I create such an object in a way to avoid the following exception:
'NSInvalidArgumentException', reason: '*** -decodeObjectForKey: cannot
be sent to an abstract object of class NSCoder: Create a concrete
instance!'
Thank you very much in advance
Use something like this, I included a property also as a demo:
class MyAwesomeViewController: UIViewController {
let someInt: Int
init(someInt: Int) {
self.someInt = someInt
super.init(nibName: nil, bundle: nil)
}
required init?(coder aDecoder: NSCoder) {
fatalError("Storyboard are a pain")
}
}
I like creating everything programmatically.
Since UIViewController inherits feom UIResponder, and UIResponder inherits from NSObject, it has empty initializer like init().
So you can just call MyAwesomeController() and it works without any errors.
If there is any the error is somewhere else like an outlet in storyboard.

NSWindowController designated initializer puzzle

I'm trying to make this code work:
class MyWindowController: NSWindowController
{
let thing: Thing
convenience init(thing: Thing)
{
self.thing = thing
super.init(windowNibName: NSNib.Name(rawValue: "MyNib"))
}
}
The problem, of course, is that a convenience initializer can't call init from a superclass. So how do I initialize my thing and still be able to call init(windowNibName:), which is itself a convenience initializer? I'd rather not have to re-implement the nib loading myself, but how do I avoid it if I can only use designated initializers?
According to the NSWindowController documentation:
You can also implement an NSWindowController subclass to avoid requiring client code to get the corresponding nib's filename and pass it to init(windowNibName:) or init(windowNibName:owner:) when instantiating the window controller. The best way to do this is to override windowNibName to return the nib's filename and instantiate the window controller by passing nil to init(window:). Using the init(window:) designated initializer simplifies compliance with Swift initializer requirements.
You can implement your class as:
class MyWindowController: NSWindowController
{
let thing: Thing
override var windowNibName: NSNib.Name? {
return NSNib.Name(rawValue: "MyNib")
}
init(thing: Thing) {
self.thing = thing
super.init(window: nil)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}

Object is nil although it was set in init

I have an object called navigator, which I set within init. I break on it to make sure it is set. However when an IBAction func, linkButtonClicked, get's called and try's to use navigator I get a nil exception. Why?
class HomeCollectionViewCell: UICollectionViewCell {
let appDelegate:AppDelegate!
let navigator: Navigator!
#IBOutlet weak var linkButton: UIButton!
var destinationView:String?
var parentViewController:UIViewController?
#IBAction func linkButtonClicked(_ sender: Any) {
do {
try self.navigator.navigate(to: self.destinationView!, from: parentViewController!)
} catch {
}
}
required init?(coder aDecoder: NSCoder) {
self.appDelegate = UIApplication.shared.delegate as! AppDelegate
self.navigator = self.appDelegate.navigator
super.init(coder: aDecoder)
}
override func prepareForReuse() {
super.prepareForReuse()
// do resetting here if needed, like empty out data
linkButton.setTitle(nil, for: .normal)
}
}
The init?(coder: NSCoder) initializer gets used when you are retrieving the object from some kind of encoded store such as Core Data. This initializer is required by the NSCoding protocol and is used only for deserializing the object. Therefore, it does not get called at object creation. It only gets called if you serialize the object using NSCoding and later deserialize it.
The function you want to override in order to ensure some value will be set in your view is not its init (and if you really want to use its init, the method to overload is init(frame:)). Instead, you should set any variables you want to be available in the viewDidLoad method of the view controller.

Instantiate NSView with custom objects in it using NSNib

I have a subclass of NSView called MyView, and I have a nib file whose File's Owner is MyView. I would like to create copies of a view in my nib file, and so I am using a class function as shown below:
class MyView: NSView {
#IBOutlet var myImageView: NSImageView! // Cocoa class
#IBOutlet var myEditingField: EditingField! // Custom subclass of cocoa object
class func initWithTitle(_ title: String) -> MyView {
let myNib = NSNib(nibNamed: "MyView", bundle: nil)
var myArray = NSArray()
myNib!.instantiate(withOwner: self, topLevelObjects: &myArray) // instantiate view and put in myArray
var myViewInstance = myArray[0] as! MyView
myViewInstance.imageView.image = NSImage(named: title)
myViewInstance.myEditingField.stringValue = title // this line
return myViewInstance
}
}
I have connected an IBOutlet from an NSImageView in the view in my nib file to the property myImageView in the MyView class, and I have connected an IBOutlet from an EditingField, a custom subclass of NSTextField that I wrote, to the myEditingField property. So, to create an instance of MyView simply I do:
let instance = MyView.initWithTitle("foo")
The issue with this method is that when IB creates the view, it is calling the required initializer init(coder:) on the EditingField in the view in the nib file. Originally, I had left my implementation of init(coder:) as simply the default fatalError("init(coder:) is not implemented") because I didn't think IB would call that initializer. I had figured IB would call init(frame:), but in reality it does call init(coder:). So, I tried implementing init(coder:) the following way:
class EditingField: NSTextField {
var id: String
// ... other properties
required init?(coder: NSCoder) {
print("init coder")
self.id = "default"
// ... other properties get default values, just like id
super.init(coder: coder)
}
}
Unfortunately, this did not work either. When I run the project using the above initializer in EditingField, the line myViewInstance.myEditingField.stringValue = title in MyView throws an error. When this happens, the debugger console reveals that the property myEditingField is nil, and, unlike myImageView, hasn't been initialized at all (despite the fact that the print message in init(coder:) still prints!)
So, my questions are (1) how do I initialize/create an NSView from a nib file that has custom objects in it? (2) why does IB call init(coder:) on EditingField? and (3) why is myEditingField nil despite the print message suggesting that the initializer ran?

init(coder:) has not been implemented in swift

I have this class file for accepting card payments
import UIKit
class PaymentViewController: UIViewController , PTKViewDelegate {
var card : STPCard
var PaymentView : PTKView
var button = UIButton.buttonWithType(UIButtonType.System) as UIButton
init(PaymentView : PTKView , button : UIButton, card : STPCard) {
self.PaymentView = PaymentView
self.button = button
self.card = card
super.init()
}
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
When I build it, it works fine, but when I execute it (run it) on my actual device , I get
fatal error: init(coder:) has not been implemented.
Any ideas ?
Based on the Inheritance Hierarchy you have setup. PaymentViewController will inherit 3 init methods.
UIViewController provides init(nibName:bundle) as its designated initializer.
UIViewController also conforms to NSCoding which is where the required init(coder:) comes from.
UIViewController also inherits from NSObject which provides a basic init() method.
The problem you are having stems from init(coder: being called when the ViewController is instantiated from a .xib or storyboard. This method is called to un-archive the .xib/storyboard objects.
From the Documentation:
iOS initializes the new view controller by calling its initWithCoder: method instead.
You should be calling the superclass designated initializer in your init method, which is init(nibName:bundle) Note: it is fine for both of those parameters to be nil. Also your init(coder:) override should call super.init(coder:)
A simple workaround will do:
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}