2016-04-08 6 views
1

Ich habe eine UIStackView Unterklasse geschrieben, aber ich habe ein seltsames Laufzeitproblem. Hier ist ein Beispielcode, wo es zu sehen ist:UIStackView-Unterklassen, die in Swift geschrieben wurden, können aufgrund nicht implementierter init (frame :) abstürzen

class SubclassedStackView: UIStackView { 

    init(text: String, subtext: String) { 
     let textlabel = UILabel() 
     let subtextLabel = UILabel() 
     textlabel.text = text 
     subtextLabel.text = subtext 
     super.init(arrangedSubviews: [textlabel, subtextLabel]) 
    } 

    required init?(coder aDecoder: NSCoder) { 
     fatalError("init(coder:) has not been implemented") 
    } 
} 

Wenn Sie dann verwenden, wie dies:

let stackView = SubclassedStackView(text: "Test", subtext: "Uh-oh!") 

Sie erhalten eine Laufzeitausnahme mit der folgenden Meldung:

fatal error: use of unimplemented initializer 'init(frame:)' for class 'test.SubclassedStackView'

Ein Blick auf die Aufrufliste zeigt, dass der Basisinitialisierer -[UIStackView initWithArrangedSubviews:] versucht, init(frame: CGRect) für die Unterklasse aufzurufen, die absichtlich nicht implementiert ist.

Natürlich könnte ich diesen zusätzlichen Initialisierer implementieren, so merkwürdig es wäre, von der Oberklasse aufgerufen zu werden, aber in meinem realen Fall würde dies mich zwingen, meine Eigenschaften zu ändern, um optionale Typen zu verwenden (oder implizit ausgepackt) optionals) wo ich das nicht machen sollte.

Ich könnte auch init(frame:) anstelle von init(arrangedSubviews:) anrufen und anschließend addArrangedSubview(view:) anrufen, um die angeordneten Unteransichten hinzuzufügen. Das Laufzeitproblem würde verschwinden, aber ich möchte keinen Frame bereitstellen.

Warum ruft der Initialisierer der Oberklasse den Initialisierer der Unterklasse auf? Kann jemand einen Weg vorschlagen, um dieses Problem zu umgehen, ohne Optionals einzuführen?

Edit: Apple bestätigte diesen Fehler, der in iOS 10 behoben werden sollte. http://www.openradar.me/radar?id=4989179939258368 gilt immer noch für iOS 8-9 leider.

+0

Das ist ähnlich zu diesem Problem (behoben in iOS9) mit UITableViewController-Unterklassen: http://stackoverflow.com/questions/25139494/how-to -subclass-uitableviewcontroller-in-swift/30719434? s = 2 | 0.1651 # 30719434 – Clafou

+0

Der obige Code kompiliert nicht mehr in iOS 10: Muss einen designierten Initialisierer der Oberklasse 'UIStackView' aufrufen –

Antwort

3

Ich bin mir nicht sicher, ob dies für Ihre Bedürfnisse funktionieren wird, aber ich habe es geschafft, auf UIStackView das Problem mit einer Erweiterung umgehen:

extension UIStackView { 
    convenience init(text: String, subtext: String) { 
     let textlabel = UILabel() 
     let subtextLabel = UILabel() 
     textlabel.text = text 
     subtextLabel.text = subtext 
     self.init(arrangedSubviews: [textlabel, subtextLabel]) 
    } 
} 

// ... 

let sv = UIStackView(text: "", subtext: "") // <UIStackView: 0x7fcd32022c20; frame = (0 0; 0 0); layer = <CATransformLayer: 0x7fcd32030810>> 
+0

Großartig für das Beispiel gegeben, aber in meinem tatsächlicher Fall muss ich Eigenschaften hinzufügen, also muss ich Unterklasse! – Clafou

1

A look at the call stack shows that the base initialiser -[UIStackView initWithArrangedSubviews:] is attempting to call init(frame: CGRect) on the subclass, which is intentionally left unimplemented.

Warum die fehlende Konstruktor nicht nur hinzufügen?

override init(frame: CGRect) { 
    super.init(frame: frame) 
} 

Das kommt das Problem von dem Rahmen initializer nicht verfügbar sein, da Sie Ihre eigenen zur Verfügung gestellt haben, und es muss, dass für die interne Umsetzung. Wenn der Frame trotzdem von AutoLayout verwaltet wird, müssen Sie sich nicht darum kümmern, was er ursprünglich eingestellt hat. Sie können ihn also nur die internen Routinen ausführen lassen, die für die Initialisierung mit Ihren Subviews erforderlich sind. Ich sehe nicht aus dem obigen Code, warum Sie Optionals hinzufügen müssten.

+0

Ich sehe kein Problem in der Basisklasse Aufruf eines anderen Initialisierer auf sich selbst, das ist sehr üblich.Aber es auf eine Unterklasse zu verweisen (nach der Reise in der Hierarchie zurück zu reisen) scheint nicht richtig zu sein und führt zu Problemen mit Swifts Initialisierungsregeln, die vor dem Aufruf von base init die Initialisierung von nicht-optionalen Eigenschaften erfordern. Mein tatsächlicher Code (der, den ich angegeben habe, war nur ein Beispiel, um den Laufzeitfehler anzuzeigen) hat nicht optionale let -Eigenschaften. – Clafou

0

init(arrangedSubviews: [] ist ein Komfort Initialisierer. Gemäß documentation müssen Sie den benannten Initialisierer der Superklasse aufrufen (init(frame:)), stattdessen

Verwandte Themen