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.
Related
I have the following data model class to represent a MyDataModel:
public class MyDataModel <T> : Codable, Comparable where T:Codable {
...
}
Next I implement a view that holds an instance of MyDataModel. I can not obviously hold a generic parameter in the subclass of UIView so I try to workaround it as follows:
protocol MyDataProtocol {
associatedtype T:Codable
var dataParam:T { get set }
}
public class MyDataView: UIView {
public var myData:some MyDataProtocol?
}
But I get an error
An 'opaque' type must specify only 'Any', 'AnyObject', protocols, and/or a base class
So I can use Any? as type for MyDataProtocol but that will still not tell me the param type of MyDataModel. I am wondering what is the solution here and right way to handle this issue.
You should set an object for myData property and the error wouldn't show.
// Protocol example
public protocol MyDataProtocol {
var dataParam: Codable? { get set }
}
class MyData: MyDataProtocol {
var dataParam: Codable?
init() {}
}
public class MyDataView: UIView {
public var myData: MyDataProtocol?
public override init(frame: CGRect) {
print("test")
super.init(frame: frame)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
MyDataView(frame: .zero)
// Opaque example
public protocol MyDataProtocol {
associatedtype T
var dataParam: T? { get set }
}
class MyData: MyDataProtocol {
var dataParam: Codable?
init() {}
}
public class MyDataView: UIView {
public var myData: some MyDataProtocol = MyData()
public override init(frame: CGRect) {
print("test")
super.init(frame: frame)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
MyDataView(frame: .zero)
I am trying to build a settings page helper class in order to simplify the setup of a settings page.
The idea would be that the class handles saving the state to UserDefaults and setting the initial state of any UISwitch.
Setting up a switch would just be a matter of setting a new switch to a class of "UISettingsSwitch" and adding the name of it to the accessibility label (it's the only identifier available as far as i'm aware).
So far I have :
import Foundation
import UIKit
class SettingsUISwitch: UISwitch {
enum SettingsType {
case darkMode, sound
}
func ison(type: SettingsType ) -> Bool {
switch type {
case .darkMode:
return userDefaults.bool(forKey: "darkMode")
case .sound:
return userDefaults.bool(forKey: "sound")
}
}
let userDefaults = UserDefaults.standard
override init(frame: CGRect) {
super.init(frame: frame)
initSwitch()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
initSwitch()
}
deinit {
}
func initSwitch() {
addTarget(self, action: #selector(toggle), for: .valueChanged)
}
#objc func toggle(){
userDefaults.setValue(self.isOn, forKey: self.accessibilityLabel!)
}
}
Not an awful lot I know.
I can currently do :
if settingsSwitch.ison(type: .darkMode) {
print (settingsSwitch.ison(type: .darkMode))
print ("ON")
} else {
print ("OFF")
}
The accessibility label doesn't seem to be available in the init setup at any point, so setting up the initial state doesn't seem to be a possibility.
Is it possible to set the initial state of the UISwitch this way ?
Ideally , I'd like to expose : settingsSwitch.darkMode.ison as a boolean ... but I can't figure that one out. Thanks for any help
I managed to use the restoration identifier to do the setup for the switch but I'd still love to remove the cases and the repeated calls to userDefaults
import Foundation
import UIKit
class UISwitchSettings: UISwitch {
enum SettingsType: String, CaseIterable {
case darkMode = "darkMode"
case sound = "sound"
}
func ison(type: SettingsType ) -> Bool {
switch type {
case .darkMode:
return userDefaults.bool(forKey: "darkMode")
case .sound:
return userDefaults.bool(forKey: "sound")
}
}
let userDefaults = UserDefaults.standard
override init(frame: CGRect) {
super.init(frame: frame)
initSwitch()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
initSwitch()
}
deinit {
}
func initSwitch() {
if let key = self.restorationIdentifier {
// Logic needs changing if default switch is off
if userDefaults.bool(forKey: key) || userDefaults.object(forKey: key) == nil {
self.isOn = true
} else {
self.isOn = false
}
}
addTarget(self, action: #selector(toggle), for: .valueChanged)
}
#objc func toggle(){
userDefaults.setValue(self.isOn, forKey: self.restorationIdentifier!)
}
}
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")
}
}
I would like to archive and unarchive a custom object in swift, something like this:
class Line : NSObject, NSCoding {
var start : CGPoint
init(start _start: CGPoint) {
start = _start
}
required init(coder aDecoder: NSCoder) {
// 1 - compile time error
self.start = aDecoder.decodeObjectForKey("start") as CGPoint
}
override init() {
}
func encodeWithCoder(aCoder: NSCoder) {
// 2 - compile time error
aCoder.encodeObject(start, forKey: "start")
}
}
The 1. compile time error is:
Type 'CGPoint' does not conform to protocol 'AnyObject'
The 2. compile time error is:
Extra argument 'forKey' in call
How to archive and unarchive a CGPoint, I know CGPoint is a struck and this is the problem, but how to solve it?
thanks in advance.
You can archive/unarchive a CGPoint using :
encodeCGPoint:forKey:
and
decodeCGPointForKey:
More information here : https://developer.apple.com/library/ios/documentation/Cocoa/Reference/Foundation/Classes/NSCoder_Class/index.html#//apple_ref/occ/instm/NSCoder/encodeCGPoint:forKey:
May be you can convert CGPoint to NSValue.
func encodeWithCoder(aCoder: NSCoder) {
var cgPointAsObject:NSValue = NSValue(CGPoint: start)
aCoder.encodeObject(cgPointAsObject, forKey: "start")
}
Similarly reverse process convert NSValue to CGpoint while decoding.
In swift3 there is some func changed, to do like this:
class UserModel: NSObject,NSCoding {
var username:String = ""
var password: Int = 0
required init(coder aDecoder: NSCoder) {
super.init()
}
override init() {
}
func encode(with aCoder: NSCoder) {
}
}
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) {
...
}