The following class has a 'let' property declared as implicitly unwrapped variable. This previously worked with Xcode 6.2:
class SubView: UIView {
let pandGestureRecognizer: UIPanGestureRecognizer!
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.pandGestureRecognizer = UIPanGestureRecognizer(target: self, action: "panAction:")
}
func panAction(gesture: UIPanGestureRecognizer) {
// ...
}
}
After updating to Xcode 6.3 (with Swift 1.2), the following compilation errors occur:
Property 'self.panGestureRecognizer' not initialized at super.init call
Immutable value 'self.panGestureRecognizer' may only be initialized once
Moving the following line before the super.init call:
self.pandGestureRecognizer = UIPanGestureRecognizer(target: self, action: "panAction:")
gives the following error:
'self' is used before super.init call
The property 'panGestureRecognizer' requires no mutation, therefore it has to be declared as constant 'let'. Since it is a constant, it has to have an initial value upon declaration, or initialize it within the init method. To initialize it, it requires to pass 'self' in the 'target' parameter.
Other thread suggested to declare it as implicitly unwrapped optional, and initialize it after the super.init call. This previously worked until I updated to Xcode 6.3.
Does anybody know a proper implementation or a workaround for this case?
The Problem
The problem is your use of let - optionals declared as let aren't given a default value of nil (var is however). The following, introduced in Swift 1.2, wouldn't be valid otherwise since you wouldn't be able to give myOptional a value after declaring it:
let myOptional: Int?
if myCondition {
myOptional = 1
} else {
myOptional = nil
}
Therefore, you're getting the error 'Property 'self.panGestureRecognizer' not initialized at super.init call' because before calling super.init(coder: aDecoder), because panGestureRecognizer isn't nil; it hasn't been initialised at all.
The Solutions:
1. Declare panGestureRecognizer as a var, meaning it will be given a default value of nil, which you could then change after calling super.init(coder: aDecoder).
2. In my opinion, the better solution: don't use an implicitly unwrapped optional and declare panGestureRecognizer with an initial value of UIPanGestureRecognizer(). Then set the target after super.init is called:
class SubView: UIView {
let panGestureRecognizer = UIPanGestureRecognizer()
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
panGestureRecognizer.addTarget(self, action: Selector("panAction:"))
}
}
You can't use self unless the class is initialized. And if you would like to use self for property initialization, it must be lazy. But lazy is not supported for let, just var.
That's because:
You must always declare a lazy property as a variable (with the var
keyword), because its initial value might not be retrieved until after
instance initialization completes. Constant properties must always
have a value before initialization completes, and therefore cannot be
declared as lazy.
It's kind of compromise and if you can live with private setter, you can do this:
class SubView: UIView {
private(set) lazy var panGestureRecognizer: UIPanGestureRecognizer = { [unowned self] in UIPanGestureRecognizer(target: self, action: "panAction:") }()
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
func panAction(gesture: UIPanGestureRecognizer) {
}
}
Or initialize panGestureRecognizer with just UIPanGestureRecognizer() and add target later.
A workaround for this specific case would be:
class SubView: UIView {
let pandGestureRecognizer: UIPanGestureRecognizer
required init(coder aDecoder: NSCoder) {
self.pandGestureRecognizer = UIPanGestureRecognizer()
super.init(coder: aDecoder)
self.pandGestureRecognizer.addTarget(self, action: "panAction:")
}
func panAction(gesture: UIPanGestureRecognizer) {
// ...
}
}
If you want to pass self to initializer of an object, you should declare your object as lazy. Because when this object is initialized, self is not ready yet.
lazy var viewModel = IntroViewModel(controller: self)
class IntroViewModel {
private weak var controller: IntroViewController?
init(controller: IntroViewController?) {
self.controller = controller
}
}
I had this problem for a different reason, it had nothing to do with Optionals or lazy. Just literally that the person object had to be initialized once.
class Person {
var name: String
init(name: String) {
self.name = name
}
}
class Account {
static let shared = Account(person: Person(name: "Bobby")) // <-- person initialized once
let person: Person = Person(name: "Lio") // initialized again!
init(person: Person) {
self.person = person
}
}
It's quite interesting that Swift can catch this error
Related
This is probably the silliest question to date - but I am having problems with initializing a subclassed view controller with a customized required coder initializer (specifically using the QRCoder pod; and to not expose code I don't own, I'll be using example classes in my case).
Here are the essentials.
class A: UIViewController {
public var name = String()
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
name = "This is a test"
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
}
Then we have...
class B: A {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
name = "What what"
}
}
If I attempt to generate a new view controller of B in say, a button tap on C...
class C: UIViewController {
let button = UIButton()
override func viewDidLoad() {
super.viewDidLoad()
button(self, action: #selector(buttonTapped(_:)), for: .touchUpInside)}
}
#objc func buttonTapped(_ sender: Any) {
let viewOfB = B()
present(viewOfB, animated: true)
}
}
It doesn't compile = because my call of let viewOfB = B() is missing the coder parameter.
The problem is, if I add a coder parameter, what in the world do I put in there? I've tried filling it with just an empty(?) NSCoder, like so
let emptyCoder = NSCoder()
let viewOfB = B(coder: emptyCoder)
But then upon a run and button tap, I get the following error:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -decodeObjectForKey: cannot be sent to an abstract object of class NSCoder: Create a concrete instance!'
I've tried adding a convenience initializer to A (where I have to run a self.init instead of super.init for some reason), but doing that gives me an EXC_BAD_ACCESS_ERROR.
What exactly do I need to provide to an init:(coder) instance to be able to... get things going?
What exactly do I need to provide to an init:(coder) instance to be able to... get things going?
Nothing. Stop thinking about init(coder:).
Here's the actual problem. When you say B(), you are calling init(). But there is no init(), because where would it come from? You didn't implement any such method in A. And it is not inherited from the superclass (UIViewController), because you effectively destroyed all inherited initializers when you implemented init(coder:).
So if you want to say B(), you must implement init() explicitly in A, yourself, like this:
class A: UIViewController {
public var name = String()
init() {
super.init(nibName: nil, bundle: nil)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
name = "This is a test"
}
}
I am trying to subclass UIButton, but i want it to be of type .system. I am struggling with initializers
class FormButton: UIButton {
var type FormButtonType: FormButtomType.oneSelection
init(oftype formType: FormButtomType) {
self.type = formType
super.init(type: .system)
}
}
problem is that I have the following error message : "Must call a designated initializer of the superclass 'UIButton'
You can't override a convenience method and call super convenience method...
As an alternative you can do a static method that return a FormButton of UIButtonType.system type.
class FormButton: UIButton {
class func newButton() -> FormButton {
return FormButton.init(type: .system)
}
}
Use it like this
let button = FormButton.newButton()
I'm guessing that you need the System type because of the highlighted animation behaviour.
If so, You can override the Highlighted property in your UIButton subclass like so:
var isHighlighted: Bool {
didSet {
// If you have images in your button, add them here same as the code below
(self.isHighlighted && self.titleLabel != nil) ? (self.titleLabel!.alpha = 0.2) : (self.titleLabel!.alpha = 1.0)
}
}
The error is little bit confusing
Must call a designated initializer of the superclass 'UIButton'
but what it is trying to say is that you need to call designated initialiser before using self. So shift self.type call after super.init call. You have created a convenience initialiser which doesn't require call to super, you need to call self.
First of all, this line is syntactically wrong.
var type FormButtonType: FormButtomType.oneSelection
should be be
var type: FormButtonType = FormButtomType.oneSelection
Now you can subclass it easily.
import UIKit
class FormButton: UIButton {
var type: FormButtonType = FormButtomType.oneSelection
// convenence initializer, simply call self if you have
// initialized type
convenience init(type: UIButtonType) {
self.init(type: type)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Now if you want to initialise some property which doesn't have default or optional value then you will need to override the designated initialiser.
For Example,
class FormButton: UIButton {
var type: UIButtonType = UIButtonType.system
var hello: String // property doesn't have initial value
// This is designated initialiser
override init(frame: CGRect) {
self.hello = "Hello"
super.init(frame: .zero)
}
convenience init(type: UIButtonType) {
self.init(type: .system)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
You need to the call superclass' designated initializer:
init(oftype formType: FormButtomType) {
super.init(frame: yourframe)
self.type = formType
}
I want to initialize a subclass of UIButton with parameters, however I am getting a EXC_BAD_INSTRUCTION error in my init(create: ; coder:) method:
class DayButton: UIButton {
var forCreateView: Bool
init(create: Bool? = false, coder: NSCoder? = nil){
self.forCreateView = create!
super.init(coder: coder!) //EXC_BAD_INSTRUCTION
}
convenience required init(coder aDecoder: NSCoder) {
self.init(create: Bool(), coder: aDecoder) //most definitely not right
}
}
In Swift its "better" to try to bypass custom subclass initiation as good as possible <- my opinion don't listen to it... :-)
For guys that anyway love it... You could create a convenience initializer, init your stuff here and call a designated initializer, to init the superclass also... below is an example...
class DayButton: UIButton {
var forCreateView: Bool = false
convenience init (forCreateView: Bool) {
self.init()
self.forCreateView = forCreateView
}
}
let myButton: UIButton = DayButton(forCreateView: false) //Usage
I have created file called MyHelper.swift
and I created class inside it:
public class MyHelper{
//..... variables here
public init(list listOfViews: [UIView]){
self.listOfViews = listOfViews
self.time = 1.0;
}
}
then i declared an object in UIViewController like this
class ViewController: UIViewController {
var myHelper: MyHelper;
override func viewDidAppear(animated: Bool) {
myHelper = MyHelper(listOfViewsToAnimatin: listOfViews)
}
// ..... rest of the code
}
but i got error that says:
**
Class "ViewController" has no initializers.
**
I tried the default fixes suggested in xcode:
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
It caused another error.
then i tried this code from internet:
required init(coder aDecoder: NSCoder!)
{
super.init(coder: aDecoder)
}
It says :
Property self.myHelper not initialized at super.init call
How on earth i can use object of MyHelper class inside UIViewController !?
This is Swift's compile time checking at work.
You'll need to either setup the MyHelper in the init method, or mark it as optional (note the question mark at the end of the var declaration):
class ViewController: UIViewController {
var myHelper: MyHelper?
required init(coder aDecoder: NSCoder!)
{
super.init(coder: aDecoder)
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
myHelper = MyHelper(listOfViewsToAnimatin: listOfViews)
}
// ..... rest of the code
}
You are initializing the MyHelper in viewDidAppear. It needs to be initialized in init (before super.init()) or you need to declare it as optional and set to nil.
var myHelper: MyHelper? = nil
You can make myHelper optional:
var myHelper:MyHelper?
When you use it, unwrap it first with:
if let myHelper = myHelper {
myHelper.yourFunction()
} else {
// self.myHelper == nil
}
Alternatively you can unwrap with !:
myHelper!.yourFunction()
But it will crash if myHelper is nil.
Assume a class that is derived from UIView as follows:
class MyView: UIView {
var myImageView: UIImageView
init(frame: CGRect) {
super.init(frame: frame)
}
init(coder aDecoder: NSCoder!) {
super.init(coder: aDecoder)
}
...
If I wanted to have the same code in both of the initializers, like
self.myImageView = UIImageView(frame: CGRectZero)
self.myImageView.contentMode = UIViewContentMode.ScaleAspectFill
and NOT duplicate that code twice in the class implementation, how would I structure the init methods?
Tried approaches:
Created a method func commonInit() that is called after super.init -> Swift compiler gives an error about an uninitialized variable myImageView before calling super.init
Calling func commonInit() before super.init fails self-evidently with a compiler error "'self' used before super.init call"
What we need is a common place to put our initialization code before calling any superclass's initializers, so what I currently using, shown in a code below. (It also cover the case of interdependence among defaults and keep them constant.)
import UIKit
class MyView: UIView {
let value1: Int
let value2: Int
enum InitMethod {
case coder(NSCoder)
case frame(CGRect)
}
override convenience init(frame: CGRect) {
self.init(.frame(frame))!
}
required convenience init?(coder aDecoder: NSCoder) {
self.init(.coder(aDecoder))
}
private init?(_ initMethod: InitMethod) {
value1 = 1
value2 = value1 * 2 //interdependence among defaults
switch initMethod {
case let .coder(coder): super.init(coder: coder)
case let .frame(frame): super.init(frame: frame)
}
}
}
I just had the same problem.
As GoZoner said, marking your variables as optional will work. It's not a very elegant way because you then have to unwrap the value each time you want to access it.
I will file an enhancement request with Apple, maybe we could get something like a "beforeInit" method that is called before every init where we can assign the variables so we don't have to use optional vars.
Until then, I will just put all assignments into a commonInit method which is called from the dedicated initialisers. E.g.:
class GradientView: UIView {
var gradientLayer: CAGradientLayer? // marked as optional, so it does not have to be assigned before super.init
func commonInit() {
gradientLayer = CAGradientLayer()
gradientLayer!.frame = self.bounds
// more setup
}
init(coder aDecoder: NSCoder!) {
super.init(coder: aDecoder)
commonInit()
}
init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
override func layoutSubviews() {
super.layoutSubviews()
gradientLayer!.frame = self.bounds // unwrap explicitly because the var is marked optional
}
}
Thanks to David I had a look at the book again and I found something which might be helpful for our deduplication efforts without having to use the optional variable hack. One can use a closure to initialize a variable.
Setting a Default Property Value with a Closure or Function
If a stored property’s default value requires some customization or setup, you can use a closure or global function to provide a customized default value for that property. Whenever a new instance of the type that the property belongs to is initialized, the closure or function is called, and its return value is assigned as the property’s default value. These kinds of closures or functions typically create a temporary value of the same type as the property, tailor that value to represent the desired initial state, and then return that temporary value to be used as the property’s default value.
Here’s a skeleton outline of how a closure can be used to provide a default property value:
class SomeClass {
let someProperty: SomeType = {
// create a default value for someProperty inside this closure
// someValue must be of the same type as SomeType
return someValue
}()
}
Note that the closure’s end curly brace is followed by an empty pair of parentheses. This tells Swift to execute the closure immediately. If you omit these parentheses, you are trying to assign the closure itself to the property, and not the return value of the closure.
NOTE
If you use a closure to initialize a property, remember that the rest of the instance has not yet been initialized at the point that the closure is executed. This means that you cannot access any other property values from within your closure, even if those properties have default values. You also cannot use the implicit self property, or call any of the instance’s methods.
Excerpt From: Apple Inc. “The Swift Programming Language.” iBooks. https://itun.es/de/jEUH0.l
This is the way I will use from now on, because it does not circumvent the useful feature of not allowing nil on variables. For my example it'll look like this:
class GradientView: UIView {
var gradientLayer: CAGradientLayer = {
return CAGradientLayer()
}()
func commonInit() {
gradientLayer.frame = self.bounds
/* more setup */
}
init(coder aDecoder: NSCoder!) {
super.init(coder: aDecoder)
commonInit()
}
init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
}
How about this?
public class MyView : UIView
{
var myImageView: UIImageView = UIImageView()
private func setup()
{
myImageView.contentMode = UIViewContentMode.ScaleAspectFill
}
override public init(frame: CGRect)
{
super.init(frame: frame)
setup()
}
required public init(coder aDecoder: NSCoder)
{
super.init(coder: aDecoder)
setup()
}
}
Does it necessarily have to come before? I think this is one of the things implicitly unwrapped optionals can be used for:
class MyView: UIView {
var myImageView: UIImageView!
init(frame: CGRect) {
super.init(frame: frame)
self.commonInit()
}
init(coder aDecoder: NSCoder!) {
super.init(coder: aDecoder)
self.commonInit()
}
func commonInit() {
self.myImageView = UIImageView(frame: CGRectZero)
self.myImageView.contentMode = UIViewContentMode.ScaleAspectFill
}
...
}
Implicitly unwrapped optionals allow you skip variable assignment before you call super. However, you can still access them like normal variables:
var image: UIImageView = self.myImageView // no error
Yet another option using a static method (added 'otherView' to highlight scalability)
class MyView: UIView {
var myImageView: UIImageView
var otherView: UIView
override init(frame: CGRect) {
(myImageView,otherView) = MyView.commonInit()
super.init(frame: frame)
}
required init(coder aDecoder: NSCoder) {
(myImageView, otherView) = MyView.commonInit()
super.init(coder: aDecoder)!
}
private static func commonInit() -> (UIImageView, UIView) {
//do whatever initialization stuff is required here
let someImageView = UIImageView(frame: CGRectZero)
someImageView.contentMode = UIViewContentMode.ScaleAspectFill
let someView = UIView(frame: CGRect(x: 0, y: 0, width: 30, height: 30))
return (someImageView, someView)
}
}
Assign myImageView in both the init() methods based on a single image create function. As such:
self.myImageView = self.createMyImageView ();
For example, like such:
class Bar : Foo {
var x : Int?
func createX () -> Int { return 1 }
init () {
super.init ()
self.x = self.createX ()
}
}
Note the 'optional' use at Int?
Additionally, if the intention is to assign myImageView exactly once, it should be a let rather than a var. That rules out some solutions that only work for var.
Another complication is multiple instance variables with dependencies between them. This rules out inline initializers calling static methods.
These requirements can be addressed by overriding with convenience initializers, which delegate to a single designated initializer:
import UIKit
class MyView: UIView {
let myImageView: UIImageView
// Just to illustrate dependencies...
let myContainerView: UIView
override convenience init(frame: CGRect) {
self.init(frame: frame, coder: nil)!
}
required convenience init?(coder aDecoder: NSCoder) {
// Dummy value for `frame`
self.init(frame: CGRect(), coder: aDecoder)
}
#objc private init?(frame: CGRect, coder aDecoder: NSCoder?) {
// All `let`s must be assigned before
// calling `super.init`...
myImageView = UIImageView(frame: CGRect.zero)
myImageView.contentMode = .scaleAspectFill
// Just to illustrate dependencies...
myContainerView = UIView()
myContainerView.addSubview(myImageView)
if let aDecoderNonNil = aDecoder {
super.init(coder: aDecoderNonNil)
} else {
super.init(frame: frame)
}
// After calling `super.init`, can safely reference
// `self` for more common setup...
self.someMethod()
}
...
}
This is based on ylin0x81's answer, which I really like but doesn't work now (build with Xcode 10.2), as load from nib crashes with:
This coder requires that replaced objects be returned from initWithCoder:
This issue is covered on a separate question, with iuriimoz's answer suggesting to add #objc to the designated initializer. That entailed avoiding the Swift-only enum used by ylin0x81.