Do the following:
Create a class called A, subclass of UITableViewCell:
class A: UITableViewCell {
var isChosen: Bool = true
}
Create a xib file and drag a UITableViewCell object as the top level object, and make sure to set its class to A:
Create an instance of A:
var a = NSBundle.mainBundle().loadNibNamed("A", owner: nil, options: nil)[0] as A
Print isChosen:
println(a.isChosen)
Output:
false
Why is this happening? It only happens when you initialize the instance from a nib.
Even if you declare the variable as an optional and set it to nil:
var isChosen: Bool! = nil
it'll still be set to false somehow.
Since your class A does not have any init methods defined swift automatically generated default initializer for you. With default init() method code var isChosen: Bool = true is a shortcut to:
class A: UITableViewCell {
var isChosen: Bool
init() {
isChosen = true
}
}
When you create your custom cell of type A from Nib then auto generated init() method does not get called because initWithCoder called hence isChosen value is false.
UPDATE:
As already mentioned by #MattGibson in comments to the question, with xCode 6 Beta 5 update we can address the problem. It can be solved by adding init with coder initializer and marking it as required, so A should contain code bellow:
required init(coder aDecoder: NSCoder!) {
super.init(coder: aDecoder)
}
How it works? From Beta 5 Release Notes:
The required modifier is written before every subclass implementation
of a required initializer. Required initializers can be satisfied by
automatically inherited initializers.
UPDATE:
required init(coder aDecoder: NSCoder!) { ... } should be added only if you override at lest one init method in your class.
Related
I get Overriding non-#objc declarations from extensions is not supported error when subclass UIImage with custom init after upgrading to Swift 4.1
class Foo: UIImage {
init(bar: String) { }
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
// Overriding non-#objc declarations from extensions is not supported
required convenience init(imageLiteralResourceName name: String) {
fatalError("init(imageLiteralResourceName:) has not been implemented")
}
}
Thanks for your help
extension UIImage {
/// Creates an instance initialized with the given resource name.
///
/// Do not call this initializer directly. Instead, initialize a variable or
/// constant using an image literal.
required public convenience init(imageLiteralResourceName name: String)
}
This init method is declared in the extension of the UIImage class.
The error pretty much says that if a function is declared in the extension than it can't be overridden in this way
class Foo: UIImage {
}
extension Foo {
convenience init(bar :String) {
self.init()
}
}
var temp = Foo(bar: "Hello")
You could try to achieve the desired result in this way.
The problem seems to be caused by the init(bar:) designed initializer, if you convert it to a convenience one, then the class will compile:
class Foo: UIImage {
convenience init(bar: String) { super.init() }
// no longer need to override the required initializers
}
Seems that once you add a designated initializer (aka a non-convenience one), Swift will also enforce overrides for all required initializers from the base class. And for UIImage we have one that lives in an extension (not sure how it got there, likely was auto-generated, as myself wasn't able to add a required initializer in an extension). And you run into the compiler error in discussion.
I've found this note in the Swift documentation about initializers:
You do not have to provide an explicit implementation of a required initializer if you can satisfy the requirement with an inherited initializer.
What is an "explicit" implementation? What is an "implicit" one then?
What does "satisfy the requirement with an inherited initializer" mean precisely?
Could you give me a code example, in which I don't have to provide an explicit implementation of a required initializer?
Here's an example with an inline explanation:
protocol JSONInitializable { // Use Encoders, but just for example
init(fromJSON: String)
}
class Foo: JSONInitializable {
let x: Int
// "required" is necessary because this init is required for the
// conformance to JSONInitializable
required init(fromJSON json: String) {
//...
x = 123 //some value from the JSON
}
}
class Baz: Foo {
// `init(fromJSON json: String)` can be inherited,
// so it's implicitly defined for Baz, as well as Foo.
}
class Bar: Foo {
// The presence of this uninitialized constant `y` requires an
// a value in the declaration, or an initializer that sets it
let y: Int
// Since we didn't specify a value for `y` in its declaration,
// this initializer must be explicitly specified so as to initialize `y`.
// Doing so blocks the inheritance of `init(fromJSON json: String)` from
// the super class, and requires us to define it ourselves,
// in order to preserve conformance to `JSONInitializable`
required init(fromJSON json: String) {
//...
y = 0
super.init(fromJSON: json)
}
}
It is saying this: If you have initialized all your properties as you declare them, there's no need to write an initializer.
Normally, when you have properties declared and not set, you write an init() method and set them there, and if there's a required initializer in the parent class, you call
super.init(possible, arg: anotherArg)
Since you don't need to set anything, you don't need to write anything, and since there's no init in your class, the super call will happen automatically. (Of course, if the required init needs values passed in, you still need to supply them.) So how is that accomplished? (see below.) The bottom line is that most of the work is done for you.
However, once you write an init(), then you're taking away this automatic behavior, and you have to make sure that those "automatic" calls are explicitly made.
Finally, before I provide an example, I should address this statement:
"if you can satisfy the requirement with an inherited initializer"
If the parent doesn't have an initializer which takes no arguments, how will it get those arguments? In that case you need initialize your class using that required super's init with the proper arguments.
Here's an example of a view controller which is not set up in IB, I would normally create it using the super's required initializer, since I didn't write an initializer for my derived class:
let vc = MagicController(nibName: nil, bundle: nil)
import UIKit
import Dotzu
class MagicController: UIViewController, UIGestureRecognizerDelegate {
let action = #selector(MagicController.buttonTapped(_:))
let action2 = #selector(MagicController.secondButtonTapped(_:))
let mainAction = #selector(MagicController.mainButtonTapped(_:))
let defaultPalette = Palette.randomPalette()
var questions = [Question]()
var startTime = TimeInterval()
var endTime = TimeInterval()
var selectedTimeIndex = 0
var selectedTagIndex = 0
var selectedRatingIndex = 0
var selectedTag = "swift"
let timeSpanDropDown = DropDown()
let ratingDropDown = DropDown()
let tagDropDown = DropDown()
var pageNumber = 1
var savedIndex = 0
var savedPostId = -1
var quotaCount = -1
var isFirstTime = true
let queryFactory = Queries(client: APIClient())
var timeSpanButton = UIBarButtonItem()
var ratingButton = UIBarButtonItem()
var tagButton = UIBarButtonItem()
var dbButton = UIBarButtonItem() // 🗄
/// start the console log, configure the main view's buttons/actions and present the UI
override func viewDidLoad() {
super.viewDidLoad()
Dotzu.sharedManager.enable()
self.edgesForExtendedLayout = []
configureButtons(container: view)
view.setNeedsLayout()
}
///configure the buttons/actions, prepare and present the UI
/// The first time this is called it sets up the buttons and drop downs
/// cleanupDataSource() has no effect the first time it is called
/// - Parameter animated: passed to super
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
if isFirstTime {
isFirstTime = false
initializeButtons()
setupDropDowns()
}
cleanupDataSource()
}
/// etc.
}
As you can see, I did not write an init for this view controller, even though a view controller has a required init. I initialized my view controller using an init() that isn't even in my code.
Trying to refactor my code in order to avoid having to reference shared instances all over the place, rather I'd like to inject them via a custom initializer. My limited understanding of initilazation in Swift is preventing me to do so.
Here's my ViewController, minus the methods and boilerplate:
class LoginVC: UIViewController {
let dataManager: DataManager
let restManager: RestManager
let defaults: UserDefaults
init(defaults: UserDefaults = .standard, dataManager: DataManager = .sharedInstance, restManager: RestManager = .sharedInstance) {
self.defaults = defaults
self.dataManager = dataManager
self.restManager = restManager
super.init(nibName: nil, bundle: nil)
}
}
I've provided default values which are shared instances declared as a static constant in their respective singleton classes of DataManager and RestManager. Was planning to do this in each and every ViewController that has these singletons referenced in them. Anyways, my problem arises as the compiler complains that I must provide a required initializer init(coder: aDecoder).
No problem, but after implementing...
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
... the compiler once again complains, this time that
Blockquote Property 'self.defaults' not initialized at super.init call
Which I guess makes sense since the required init? is, as far as I know, a delegating init (could be that I'm totally mistaken here). I do not know how to pass the default params of my custom init to the super init of this initializer, or if that's even possible.
I have tried a workaround though by changing the constants to mutable variables and by making them implicitly unwrapped like so:
class LoginVC: UIViewController {
var dataManager: DataManager!
var restManager: RestManager!
var defaults: UserDefaults!
init(defaults: UserDefaults = .standard, dataManager: DataManager = .sharedInstance, restManager: RestManager = .sharedInstance) {
self.defaults = defaults
self.dataManager = dataManager
self.restManager = restManager
super.init(nibName: nil, bundle: nil)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
}
This workaround actually manages to compile and build, but the app crashes as soon as userDefaults which are in viewDidLoad are unwrapped because they have a nil value. All of them have nil values even though they are defaulted in the params of the custom initializer which leads me to believe that I've royally screwed up.
Any help/clarification would be greatly appreciated, thanks in advance.
Not sure that this is what you would like to do, but I would suggest you this solution
extension UIViewController {
var defaults: UserDefaults {
get {
return .standard
}
}
}
and so in each UIViewController you can do self.defaults
If you want some of your UIViewControllers to have another references, you can do this
//create protocol with all vars
protocol References {
var defaults: UserDefaults {get}
}
//create extension with default implementation
extension References {
var defaults: UserDefaults {
get {
return .standard
}
}
}
//add this protocol to all UIViewControllers
extension UIViewController: References {
}
//or create new class with references
class ReferenceViewController: UIViewController, References {
}
//override reference in some view controller if needed
class AnotherReferencesViewController: UIViewController { //or ReferenceViewController
var defaults: UserDefaults {
get {
//something new
return .standard
}
}
}
Assuming the following class structure:
class A : SKSpriteNode
{
let foo : CGFloat
required init?(coder aDecoder: NSCoder) { ... }
init(foo: CGFloat)
{
self.foo = foo
super.init(
texture: SKTexture(imageNamed: "test"),
color: UIColor.blackColor(),
size: CGSizeMake(42, 42))
}
}
class B : A
{
required init?(coder aDecoder: NSCoder) { ... }
init()
{
super.init(foo: 42)
}
}
With A and B being in different modules, shows the following compiler error in B:
Initializer 'init()' overrides Objective-C method 'init' from superclass 'SKSpriteNode'
However, when changing init() in B to override init(), the following error occurs:
Initializer does not override a designated initializer from its superclass
Which is only solvable by adding init() in A which in turn calls the init of the base class.
Observations:
The above code did work in Swift 1.1 (i.e. without override).
It does work, when A and B are in the same module.
It does work, when overriding init() in A and using override init() in B.
The release notes state:
Imported C structs now have a default initializer in Swift, which initializes all of the struct's fields to zero.
But IMO this is not related, since SKSpriteNode is not a struct.
Is this a compiler bug or did I miss something else in the release notes?
Update Apple fixed this in Xcode 6.3 Beta 2.
I have a subclass of UIButton as follows:
class VectorizedButton: UIButton {
override var highlighted: Bool {
didSet {
setNeedsDisplay()
}
}
}
Everything works great, until I added this line in my root controller:
var twitterButton: TwitterButton?
TwitterButton extends VectorizedButton.
Here is the error I get:
...UIView+Vectorized.swift:42:7: Class 'VectorizedButton' has no initializers
...UIView+Vectorized.swift:44:18: Stored property 'highlighted' without initial value prevents synthesized initializers
Easy, let's set a default value:
override var highlighted: Bool = false
Tougher error:
<unknown>:0: error: super.init called multiple times in initializer
<unknown>:0: error: super.init called multiple times in initializer
<unknown>:0: error: super.init called multiple times in initializer
Try overriding init?
required init(coder aDecoder: NSCoder) {
highlighted = false
super.init(coder: aDecoder)
}
Even more errors:
error: 'self' used before super.init call
highlighted = false
^
error: 'self' used before super.init call
highlighted = false
^
Anyone cares to explain what's going on here?
You just had to add the required init
Here is what I did :
class VectorizedButton: UIButton {
override var highlighted: Bool {
didSet {
setNeedsDisplay()
}
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
}
class TwitterButton: VectorizedButton {
}
And in a ViewController :
var twitterButton: TwitterButton?
Works fine like this.
You wanted to override the highlighted property and create the twitterButton as Optional, so you need to implement an init in your class.
You cannot set the highlighted property with this :
override var highlighted: Bool = false
You need to set it in the init, or like this :
override var highlighted: Bool {
get { return false }
set { super.highlighted = newValue }
}
And for this :
required init(coder aDecoder: NSCoder) {
highlighted = false
super.init(coder: aDecoder)
}
You need to set your property after the call to super.init.