How can I override convenience init in UIAlertController for swift?
you are not overriding any convenience init, it looks like you are creating a new one.
convenience init(title: String?, message: String?, preferredStyle: UIAlertControllerStyle, colorCode: String?){
self.init(title: title, message: message, preferredStyle: preferredStyle)
}
Is probably what you want, you just need to handle the color code
Looks like you are going to have to go a round about way:
First create a create extension
extension UIAlertController
{
class func create(title: String?, message: String?, preferredStyle: UIAlertControllerStyle) -> AnyObject
{
return UIAlertController(title: title, message: message, preferredStyle: preferredStyle);
}
}
Then in the ColorAlertViewController, you will create another function to create this object:
class func createWithColor(title: String?, message: String?, preferredStyle: UIAlertControllerStyle, colorCode: String?) -> AnyObject
{
var c = super.create(title, message: message, preferredStyle: preferredStyle);
//handle color code here
return c;
}
Now anywhere you want to create this object, just call
var colorAlertView = ColorAlertViewController.createWithColor("title Name", message: "the message", preferredStyle: .ActionSheet, colorCode: "the color");
of course this won't work inside the UI builder, you would have to create this via code.
Initialising a subclass of UIAlertController
You should not subclass UIAlertController
.
Check this link:
https://developer.apple.com/documentation/uikit/uialertcontroller#//apple_ref/doc/uid/TP40014538-CH1-SW2
you can add methods using extensions
but should not subclass it.
Convenience Init Override
The reason override
is unneeded:
Conversely, if you write a subclass initializer that matches a superclass convenience initializer, that superclass convenience initializer can never be called directly by your subclass, as per the rules described above in Initializer Chaining. Therefore, your subclass is not (strictly speaking) providing an override of the superclass initializer. As a result, you do not write the
override
modifier when providing a matching implementation of a superclass convenience initializer.
But as written, it seems it should work — as far as I can tell this is a compiler bug. If you change the name of the array
argument to ClassB's initializer to e.g. array2
, then it works as expected. You should file a Radar!
Adding Convenience Initializers in Swift Subclass
My understanding of Initializer Inheritance is the same as yours, and I think we are both well aligned with what the book states. I don't think it's an interpretation issue or a misunderstanding of the stated rules. That said, I don't think you're doing anything wrong.
I tested the following in a Playground and it works as expected:
class RectShape: NSObject {
var size = CGSize(width: 0, height: 0)
convenience init(rectOfSize size: CGSize) {
self.init()
self.size = size
}
}
class SquareShape: RectShape {
convenience init(squareOfSize size: CGFloat) {
self.init(rectOfSize: CGSize(width: size, height: size))
}
}
RectShape
inherits from NSObject
and doesn't define any designated initializers. Thus, as per Rule 1, it inherits all of NSObject
's designated initializers. The convenience initializer I provided in the implementation correctly delegates to a designated initializer, prior to doing the setup for the intance.
SquareShape
inherits from RectShape
, doesn't provide a designated initializer and, again, as per Rule 1, inherits all of SquareShape
's designated initializers. As per Rule 2, it also inherits the convenience initializer defined in RectShape
. Finally, the convenience initializer defined in SquareShape
properly delegates across to the inherited convenience initializer, which in turn delegates to the inherited designated initializer.
So, given the fact you're doing nothing wrong and that my example works as expected, I am extrapolating the following hypothesis:
Since SKShapeNode
is written in Objective-C, the rule which states that "every convenience initializer must call another initializer from the same class" is not enforced by the language. So, maybe the convenience initializer for SKShapeNode
doesn't actually call a designated initializer. Hence, even though the subclass MyShapeNode
inherits the convenience initializers as expected, they don't properly delegate to the inherited designated initializer.
But, again, it's only a hypothesis. All I can confirm is that the mechanics works as expected on the two classes I created myself.
Does Convenience Init need to have same number of parameters as Required Init
There is no correlation in number of parameters between designated (or even required) initializers and convenience initializers.
Convenience initializers are simply those that need to forward initialization to one of designated initializers. How many input parameters will it have is completely dependent on implementation and use-case. It may be more, less or equal.
It is hard to find a simple-enough example to demonstrate all of those but consider something like this:
class HighlightedWordContainer {
let words: [String]
var highlightedWord: String
init(words: [String], highlighted: String) {
self.words = words
self.highlightedWord = highlighted
}
init(words: [String], highlightedIndex: Int) {
self.words = words
self.highlightedWord = words[highlightedIndex]
}
convenience init(singleWord: String) {
self.init(words: [singleWord], highlighted: singleWord)
}
convenience init(word1: String, word2: String, word3: String, highlighted: String) {
self.init(words: [word1, word2, word3], highlighted: highlighted)
}
convenience init(wordsSeparatedByWhitespace: String, highlightedIndex: Int) {
let words = wordsSeparatedByWhitespace.components(separatedBy: .whitespaces)
self.init(words: words, highlightedIndex: highlightedIndex)
}
convenience init?(descriptor: [String: Any], keys: [String], selectedKey: String) {
let words: [String] = keys.compactMap { descriptor[$0] as? String }
guard words.isEmpty == false else { return nil }
guard let selectedWord = descriptor[selectedKey] as? String else { return nil }
guard let selectedWordIndex = words.firstIndex(of: selectedWord) else { return nil }
self.init(words: words, highlightedIndex: selectedWordIndex)
}
}
Here I created a class with 2 designated initializers. These two need to set all properties that are not already set by default. Which means they need to set both words
and highlightedWord
. A designated initializer may not delegate a call to another designated initializer so the following will not work:
init(words: [String], highlightedIndex: Int) {
self.init(words: words, highlighted: words[highlightedIndex])
}
And all convenience initializers do need to call any of the designated initializers and may also not directly set or use self
properties UNTIL a designated constructor is being called. So:
convenience init(wordsSeparatedByWhitespace: String, highlightedIndex: Int) {
let words = wordsSeparatedByWhitespace.components(separatedBy: .whitespaces)
// print(highlightedWord) // NOT OK!
self.init(words: words, highlightedIndex: highlightedIndex)
print(highlightedWord)
}
And from the example I hope it makes clear that a convenience initializer can have more, fewer or same number of input parameters. It all just depends.
Some more plausible examples:
class Point {
let x: Int
let y: Int
init(x: Int, y: Int) { self.x = x; self.y = y }
convenience init(x: Int) { self.init(x: x, y: 0) }
convenience init(y: Int) { self.init(x: 0, y: y) }
convenience init(polar: (radius: Double, angle: Double)) { self.init(x: Int(cos(polar.angle)*polar.radius), y: Int(sin(polar.angle)*polar.radius)) }
}
class NumericValueAsString {
let stringValue: String // A value represented as "123.456"
init(stringValue: String) { self.stringValue = stringValue }
convenience init(value: Int) { self.init(stringValue: .init(value)) }
convenience init(integerPart: String, fractionPart: String) { self.init(stringValue: integerPart + "." + fractionPart) }
}
Also; required
keyword has nothing to do with anything in this context. You can place it to any of the initializers (designated or convenience), to multiple of them or even all of them.
Adding additional info based on a comment:
your examples shows that you can have a convenience init that can pass
parameters back to the required init``. what about instances where I
there is no convenience init``` that can pass a value to parameter to
one which is in required init but not in the convenience init. Like my
speed parameter in my original question.
This really depends on the interface of your class which is not fully shown in your example. For instance, is speed
publicly exposed?
If speed is public then what you are looking for is something along the lines
let itemInstance = MyItem()
itemInstance.timeStamp = timeStamp
itemInstance.position = position
itemInstance.distance = distance
itemInstance.speed = speed
or
let itemInstance = MyItem(timeStamp: timeStamp, position: position, distance: distance)
itemInstance.speed = speed
or even create a new convenience constructor
extension MyItem {
convenience init(timeStamp: FitTime? = nil,
position: Position? = nil,
distance: Measurement<UnitLength>? = nil
speed: Speed? = nil
) {
self.init(timeStamp: timeStamp, position: position, distance: distance)
self.speed = speed
}
}
let itemInstance = MyItem(timeStamp: timeStamp, position: position, distance: distance, speed: speed)
But if your speed
property is privately defined than you can not assign to it anywhere but internally. You need to be able to modify your class so that it can accept this property somewhere. Either via constructor, via property or via a method. Until there is no access at all you can not expect to be able to modify it.
But if this is your own class I would turn things upside down if possible. Your main constructor should be the one that accepts all 4 parameters. But your convenience initializer should be required (Yes, convenience initializer may be required). So in that case you would have:
public required convenience init() {
self.init(timeStamp: self, position: self, distance: self, speed: self)
}
public init(timeStamp: FitTime? = nil,
position: Position? = nil,
distance: Measurement<UnitLength>? = nil,
speed: Speed?) {
super.init()
self.timeStamp = timeStamp
self.position = position
self.distance = distance
self.speed = speed
}
}
This specific case feels like it is missing something compared to your code but again, you did not provide full definition of your class and with it does or is supposed to do. For instance in the code you provided if one would call a convenience initializer then the following lines would execute in this order:
self.$timeStamp.owner = self // from self.init()
self.timeStamp = timeStamp
which looks like a problem to begin with.
So to wrap it up. There are very many ways you can achieve what you are asking for. But it all depends on what you are trying to build here.
I hope it at least puts you in the right track.
Initializer does not override a designated initializer from its superclass
My solution is a quick fix, but I think is easier than what Apple purposes on the the Release Notes. For more information search for 19775924 http://adcdownload.apple.com//Developer_Tools/Xcode_6.3_beta_3/Xcode_6.3_beta_3_Release_Notes.pdf here. What Apple says is that you create an Objective-C file and extend it (having to add it to the header files and all) and it's on "Known Issues in Xcode 6.3 beta 3", so I think is easy to do what I did:
This is how I fixed it for UIButton
:
class CustomButton : UIButton {
init() {
super.init(frame: CGRectZero)
}
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
And this is one of my ViewControllers (remove public if not needed):
public class GenericViewController: UIViewController {
public init() {
super.init(nibName: nil, bundle: nil)
}
required public init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
I don't use IB so I also have UIView
, because I do separate the view from the viewController
(remove public if not needed):
public class GenericMenuView: UIView {
public init() {
super.init(frame: CGRectZero)
}
public required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
I need this specially in views because I have a setupViews
method that I override in all subclasses that is called on the init. And using AutoLayout I don't need any frames (so I don't override the init with the frame parameter).
So it seems you have to drop override
. Oh! and be sure to not call self.init()
or the class is never initialized (and it crashes after some internal timeout).
Related Topics
Error Domain=Kclerrordomain Code=2 "The Operation Couldn't Be Completed. (Kclerrordomain Error 2.)"
My App Is Getting Crashed on UIdocumentpickerviewcontroller
Adding an Skscene to a Uiviewcontroller
Applying Different Attributes for Different Portions of an Nsattributedstring
iOS 8 Uitableview First Row Has Wrong Height
How to Detect If My Device Is an iPhone x in Swift 4
Can't Use @Observedobject on Real iPhone
Multiple Async Requests at Once in Objective C
Uicollectionview Inside Tableviewcell Not Called
How to Upload Multiple Images in Multipart Using Alamofire
iOS Today Extension with Core Data
Xcode UI Test Uikeyinput Typetext
Submit Watchkit Provisioning Error
Collide Type Source Error - Spritekit Swift Game
Value of Type 'string' Has No Member 'stringbytrimmingcharactersinset'
Media_Err_Decode on HTML5 Video in iOS UIwebview After Many Plays
Pass a Nsdictionary as Parameter to UItapgesturerecognizer
Save Scrollviews Position and Scroll Back to It Later (Offset to Position)