UIViewController with closure init - ios

I try to create UIViewController:
class CategoriesVC: UIViewController {
let tableView = UITableView()
var completionHandler: (Category)->Void?
init(completionHandler: #escaping (Category)->Void) {
super.init()
self.completionHandler = completionHandler
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
and I get this error:
Must call a designated initializer of the superclass 'UIViewController'
On this line:
super.init()

The error states clearly that you must call the designate init for UIViewController, which in this case is super.init(nibName:,bundle:).
Also, the completionHandler syntax is wrong, here's the fix:
class CategoriesVC: UIViewController {
let tableView = UITableView()
var completionHandler: ((Category)->Void)?
init(completionHandler: #escaping ((Category)->Void)) {
super.init(nibName: nil, bundle: nil)
self.completionHandler = completionHandler
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}

Related

Saving and restoring app state with native API

I'm trying to implement saving/restoring state of application.
That code I have:
In AppDelegate I've added:
func application(_ application: UIApplication, shouldRestoreApplicationState coder: NSCoder) -> Bool {
return true
}
func application(_ application: UIApplication, shouldSaveApplicationState coder: NSCoder) -> Bool {
return true
}
Then I have TabBarController implementation that conforms to restoring:
final class TabBarViewController: UITabBarController {
init() {
super.init(nibName: nil, bundle: nil)
restorationIdentifier = "ContainerVC"
restorationClass = type(of: self)
let vc1 = ViewController(with: .green)
let vc2 = ViewController(with: .red)
vc1.tabBarItem = .init(title: "green", image: nil, tag: 0)
vc2.tabBarItem = .init(title: "red", image: nil, tag: 1)
self.viewControllers = [vc1, vc2]
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
extension TabBarViewController: UIViewControllerRestoration {
static func viewController(withRestorationIdentifierPath identifierComponents: [String], coder: NSCoder) -> UIViewController? {
let vc = TabBarViewController()
vc.selectedIndex = coder.decodeInteger(forKey: "index")
return vc
}
override func encodeRestorableState(with coder: NSCoder) {
coder.encode(self.selectedIndex, forKey: "index")
print("encoded")
super.encodeRestorableState(with: coder)
}
override func decodeRestorableState(with coder: NSCoder) {
self.selectedIndex = coder.decodeInteger(forKey: "index")
super.decodeRestorableState(with: coder)
}
}
Also TabBarViewController is a root ViewController of app window.
I want to, for example, i select second tab - after app terminated its restored and app launched again it should show second tab opened.
I think that encoding should be performed when app terminates. But it's not called. And Decoding not called too. What I make wrong? Thanks in advance!

Custom init UIViewController query

I am hoping you can help me understand why the below code segment works and the other does not. I am wanting to create a custom initialiser for my UIViewController which has a custom nib file I have created.
My issue is that I want to understand why in the below code the references to newMember and facebookLogin are retained when I hit the viewDidLoad method but in the other segment of code they are not? Can anyone shed some light as to why this would be the case?
Working Code Block
class RegistrationFormViewController: MiOSBaseViewController
{
var newMember:Member!
var facebookLogin: Bool = false
init(member: Member, facebookLogin: Bool = false) {
self.newMember = member
self.facebookLogin = facebookLogin
super.init(nibName: "RegistrationFormViewController", bundle: nil)
}
required init?(coder aDecoder: NSCoder) {
super.init(nibName: "RegistrationFormViewController", bundle: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
let view = self.view as! RegistrationFormView
view.loadViewWith(member: newMember)
view.customNavBarView.backActionBlock = {
self.newMember.deleteEntity(MiOSDataContext.sharedInstance.managedObjectContext)
_ = self.navigationController?.popViewController(animated: true)
return
}
}
}
Broken Code Block
class RegistrationFormViewController: MiOSBaseViewController
{
var newMember:Member!
var facebookLogin: Bool = false
init(member: Member, facebookLogin: Bool = false) {
self.newMember = member
self.facebookLogin = facebookLogin
super.init(nibName: "RegistrationFormViewController", bundle: nil)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func viewDidLoad() {
super.viewDidLoad()
let view = self.view as! RegistrationFormView
view.loadViewWith(member: newMember)
view.customNavBarView.backActionBlock = {
self.newMember.deleteEntity(MiOSDataContext.sharedInstance.managedObjectContext)
_ = self.navigationController?.popViewController(animated: true)
return
}
}
}
Thanks,
Michael

How to send a complete closure better

What I want is to send a closures to a UIViewController to tell it what it will do in the end. But when it comes to a package of UIViewControllers it will be a little messy.
class ViewController: UIViewController {
private var complete: ()->()
init(complete: ()->()){
self.complete = complete
super.init(nibName: nil, bundle: nil)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
class ViewController2: UIViewController {
private var complete: ()->()
init(complete: ()->()){
self.complete = complete
super.init(nibName: nil, bundle: nil)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Here are my UIViewControllers. What want to do is to send a complete closure to UIViewController2, but I have to push UIViewController first. So what I have to do is send the closure to UIViewController, then UIViewController send the closure to UIViewController2. It is not messy when there are only two UIViewControllers. But it will be very messy when there comes out a package of UIViewControllers.
Any better solutions?
You the following code to handle the completion handler
First, create a base class of UIViewController and define the completionHandler
import UIKit
public class MyController: UIViewController {
var completionHandler: ((Bool, String) -> Void)? // Bool and String are the return type, you can choose anything you want to return
public func onComplete(completion : ((Bool, String) -> Void)?)
{
self.completionHandler = completion
}
override public func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override public func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
In ViewController1, you need to call another ViewController like this
class ViewController: MyController {
// Initialization of view objects
override func viewDidLoad()
{
super.viewDidLoad()
//You are loading another ViewController from current ViewController
let controller2 = self.storyboard?.instantiateViewControllerWithIdentifier("controller2") as? MyController
controller2?.onComplete({ (finished, event) in
self.completionHandler?(finished, event)
})
}
//This is your button action or could be any event on which you will fire the completion handler
#IBAction func buttonTapped()
{
self.completionHandler(boolType, controllerName)
}
and where ever, you will create a new ViewController you will need to set its completionHandler by writing the line
controller2?.onComplete({ (finished, event) in
self.completionHandler?(finished, event)
})

Cannot subclass WKWebView

I am trying to subclass WKWebView. When I implement my own initializer, I got this error:
'required' initializer 'init(coder:)' must be provided by subclass of 'WKWebView'
Ok, that is well known that we have to implement it for subclasses of UIView. For a direct subclass of UIView it works just implementing it, but with WKWebView it does not seem so simple. I followed the Fix-it hint, and this snippet is added to the code:
required #availability(*, unavailable) convenience init!(coder: NSCoder!) {
fatalError("init(coder:) has not been implemented")
}
So I get a class like the following:
import WebKit
class TSWebView : WKWebView {
let s: String
let i: Int
init(s: String, i: Int) {
self.s = s
self.i = i
super.init(frame: CGRectZero, configuration: WKWebViewConfiguration())
}
required #availability(*, unavailable) convenience init!(coder: NSCoder!) {
fatalError("init(coder:) has not been implemented")
}
}
However, when I do this I get these four other errors:
expected declaration
required #availability(*, unavailable) convenience init!(coder: NSCoder!) {
consecutive declarations on a line must be separated by ';'
required #availability(*, unavailable) convenience init!(coder: NSCoder!) {
cannot override 'init' which has been marked unavailable
required #availability(*, unavailable) convenience init!(coder: NSCoder!) {
'required' modifier must be present on all overrides of a required initializer
required #availability(*, unavailable) convenience init!(coder: NSCoder!) {
Any ideas? My Xcode Version is 6.1.1 (6A2008a). Thanks a lot.
Just override the regular initialization like this. This worked for me, Swift 5.
override init(frame: CGRect, configuration: WKWebViewConfiguration) {
super.init(frame: frame, configuration: configuration)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
This is totes possible. You must only use convenience initializers and properties with default values set:
import WebKit
class MPWebView : WKWebView {
var transparent: Bool = false
convenience init(config: WKWebViewConfiguration = WKWebViewConfiguration()) {
let prefs = WKPreferences()
prefs.plugInsEnabled = true // NPAPI for Flash, Java, Hangouts
prefs.minimumFontSize = 14
prefs.javaScriptCanOpenWindowsAutomatically = true;
config.preferences = prefs
config.suppressesIncrementalRendering = false
self.init(frame: CGRectZero, configuration: config)
}
convenience required init(url: NSURL) {
self.init(config: nil)
loadRequest(NSURLRequest(URL: url))
}
}
Try taking out the extra decorations:
import WebKit
class TSWebView : WKWebView {
let s: String
let i: Int
init(s: String, i: Int) {
self.s = s
self.i = i
super.init(frame: CGRectZero, configuration: WKWebViewConfiguration())
}
convenience init!(coder: NSCoder!) {
super.init(coder:coder)
}
}
Although I'm guessing the whole point of the "availablity(*, unavailable)" is to make it so that you can't invoke the initializer (and hence can't effectively subclass WKWebView.

Swift: Does not conform to protocol NSCoding

I am trying to use the NSCoding protocol on a class I have written in swift, but cannot seem to figure out why the compiler complains that it "does not conform to protocol NSCoding" when I do implement the required methods:
class ServerInfo: NSObject, NSCoding {
var username = ""
var password = ""
var domain = ""
var location = ""
var serverFQDN = ""
var serverID = ""
override init() {
}
init(coder aDecoder: NSCoder!) {
self.username = aDecoder.decodeObjectForKey("username") as NSString
self.password = aDecoder.decodeObjectForKey("password") as NSString
self.domain = aDecoder.decodeObjectForKey("domain") as NSString
self.location = aDecoder.decodeObjectForKey("location") as NSString
self.serverFQDN = aDecoder.decodeObjectForKey("serverFQDN") as NSString
self.serverID = aDecoder.decodeObjectForKey("serverID") as NSString
}
func encodeWithCoder(_aCoder: NSCoder!) {
_aCoder.encodeObject(self.username, forKey: "username")
_aCoder.encodeObject(self.password, forKey: "password")
_aCoder.encodeObject(self.domain, forKey: "domain")
_aCoder.encodeObject(self.location, forKey: "location")
_aCoder.encodeObject(self.serverFQDN, forKey: "serverFQDN")
_aCoder.encodeObject(self.serverID, forKey: "serverID")
}
}
Is this a bug or am I just missing something?
As you can see in the detailed compiler messages in the Report navigator,
your methods are not declared correctly:
error: type 'ServerInfo' does not conform to protocol 'NSCoding'
class ServerInfo: NSObject, NSCoding {
^
Foundation.NSCoding:2:32: note: protocol requires function 'encodeWithCoder' with type '(NSCoder) -> Void'
#objc(encodeWithCoder:) func encodeWithCoder(aCoder: NSCoder)
^
note: candidate has non-matching type '(NSCoder!) -> ()'
func encodeWithCoder(_aCoder: NSCoder!) {
^
Foundation.NSCoding:3:25: note: protocol requires initializer 'init(coder:)' with type '(coder: NSCoder)'
#objc(initWithCoder:) init(coder aDecoder: NSCoder)
^
note: candidate has non-matching type '(coder: NSCoder!)'
init(coder aDecoder: NSCoder!) {
(This may have changed between the beta releases.)
In addition, the initWithCoder method has to be marked as required:
required init(coder aDecoder: NSCoder) { }
func encodeWithCoder(_aCoder: NSCoder) { }
In Swift 3 the required methods are
required init(coder aDecoder: NSCoder) { }
func encode(with aCoder: NSCoder) { }
The parameters are not implicitly unwrapped (remove the !), and the initializer requires the required modifier:
required init(coder aDecoder: NSCoder) {
...
func encodeWithCoder(_aCoder: NSCoder) {
For Swift 3
A minor but important change has been commited . The init method is same but the encodeWithCoder method has been modified.
required init(coder aDecoder: NSCoder) {
...
}
func encode(with _aCoder: NSCoder) {
...
}
For Swift 3 (on Xcode 8.2 beta (8C23))
It appears to have changed again. This is the only variation that I could get to work...
func encodeWithCoder(_ _aCoder: NSCoder) {
...
}

Resources