2016-02-10 4 views
33

Ich bin für Unterklasse von UIViewController zum Schreiben eigener init Problem hat, im Grunde möchte ich für Viewcontroller die Abhängigkeit durch die init-Methode übergeben, anstatt-Eigenschaft direkt wie viewControllerB.property = valueGewohnheit für UIViewController in Swift mit Schnittstelle Setup init in Storyboard

Also machte ich ein benutzerdefiniertes init für meine Viewcontroller und Super init

init(meme: Meme?) { 
     self.meme = meme 
     super.init(nibName: nil, bundle: nil) 
    } 

die Schnittstelle in Storyboard befindet view-Controller bezeichnet nennen, ich habe auch die Schnittstelle für benutzerdefinierte Klasse machen meine view-Controller zu sein. Und Swift muss diese Init-Methode aufrufen, auch wenn Sie innerhalb dieser Methode nichts tun. Ansonsten wird der Compiler beschweren ...

required init?(coder aDecoder: NSCoder) { 
     super.init(coder: aDecoder) 
    } 

Das Problem ist, wenn ich versuche, meine Gewohnheit zu nennen init mit MyViewController(meme: meme) es keine Eigenschaften hat init in meinem Viewcontroller überhaupt ...

Ich habe versucht, zu debuggen, ich habe in meinem ViewController gefunden, init(coder aDecoder: NSCoder) zuerst aufgerufen, dann wird meine benutzerdefinierte init später aufgerufen. Diese beiden Init-Methoden geben jedoch unterschiedliche self Speicheradressen zurück.

Ich vermute etwas falsch mit der init für meine ViewController, und es wird immer self mit der init?(coder aDecoder: NSCoder), die, hat keine Implementierung.

Kann jemand benutzerdefinierte init für Ihren ViewController richtig machen? Hinweis: meine Viewcontroller-Schnittstelle ist hier in Storyboard

eingerichtet ist meine Viewcontroller-Code:

class MemeDetailVC : UIViewController { 

    var meme : Meme! 

    @IBOutlet weak var editedImage: UIImageView! 

    // TODO: incorrect init 
    init(meme: Meme?) { 
     self.meme = meme 
     super.init(nibName: nil, bundle: nil) 
    } 

    required init?(coder aDecoder: NSCoder) { 
     super.init(coder: aDecoder) 
    } 

    override func viewDidLoad() { 
     /// setup nav title 
     title = "Detail Meme" 

     super.viewDidLoad() 
    } 

    override func viewWillAppear(animated: Bool) { 
     super.viewWillAppear(animated) 
     editedImage = UIImageView(image: meme.editedImage) 
    } 

} 
+0

haben Sie eine Lösung dafür bekommen? – user481610

Antwort

5

Ursprünglich gab es ein paar Antworten, die Kuh waren gestimmt und gelöscht, obwohl sie im Grunde richtig waren. Die Antwort ist, du kannst nicht.

Beim Arbeiten mit einer Storyboard-Definition werden alle View Controller-Instanzen archiviert. Um sie zu initialisieren, ist es erforderlich, dass init?(coder... verwendet wird. Die coder ist, woher alle Einstellungen/Ansicht Informationen stammen.

So, in diesem Fall ist es nicht möglich, einige andere init-Funktion auch mit einem benutzerdefinierten Parameter aufzurufen. Es sollte entweder als Eigenschaft festgelegt werden, wenn das Segment vorbereitet wird, oder Sie können Segmente ablegen und die Instanzen direkt vom Storyboard laden und konfigurieren (im Grunde ein Factory-Muster mit einem Storyboard).

In allen Fällen verwenden Sie die vom SDK benötigte init-Funktion und übergeben anschließend weitere Parameter.

1

UIViewController Klasse NSCoding Protokoll entsprechen, die wie folgt definiert ist:

public protocol NSCoding { 

    public func encode(with aCoder: NSCoder) 

    public init?(coder aDecoder: NSCoder) // NS_DESIGNATED_INITIALIZER 
}  

So UIViewController hat zwei Designated initializer init?(coder aDecoder: NSCoder) und init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?).

Storyborad ruft init?(coder aDecoder: NSCoder) direkt zu init UIViewController und UIView, Es gibt keinen Platz für Sie Parameter zu übergeben.

Eine umständliche Abhilfe ist, einen temporären Cache zu verwenden:

class TempCache{ 
    static let sharedInstance = TempCache() 

    var meme: Meme? 
} 

TempCache.sharedInstance.meme = meme // call this before init your ViewController  

required init?(coder aDecoder: NSCoder) { 
    super.init(coder: aDecoder); 
    self.meme = TempCache.sharedInstance.meme 
} 
12

Sie können keinen benutzerdefinierten initializer verwenden, wenn Sie von einem Storyboard zu initialisieren, mit init?(coder aDecoder: NSCoder) ist, wie Apple das Storyboard entwickelt, um eine Steuerung zu initialisieren. Es gibt jedoch Möglichkeiten, Daten an eine UIViewController zu senden.

Der Name Ihres View-Controllers ist detail darin, also nehme ich an, dass Sie von einem anderen Controller kommen. In diesem Fall können Sie die prepareForSegue Methode verwenden, um Daten zum Detail zu senden (Dies ist Swift 3):

override func prepare(for segue: UIStoryboardSegue, sender: AnyObject?) { 
    if segue.identifier == "identifier" { 
     if let controller = segue.destinationViewController as? MemeDetailVC { 
      controller.meme = "Meme" 
     } 
    } 
} 

habe ich nur eine Eigenschaft vom Typ String statt Meme für Testzwecke. Stellen Sie außerdem sicher, dass Sie die richtige Überleitungskennung übergeben ("identifier" war nur ein Platzhalter).

8

Eine Möglichkeit, die ich getan habe, ist mit einem Komfort Initialisierer.

class MemeDetailVC : UIViewController { 

    convenience init(meme: Meme) { 
     self.init() 
     self.meme = meme 
    } 
} 

Dann Sie initialisieren Ihre MemeDetailVC mit let memeDetailVC = MemeDetailVC(theMeme)

Apple's documentation on initializers ist ziemlich gut, aber mein persönlicher Favorit ist die Ray Wenderlich: Initialization in Depth Tutorial-Serie, die Ihnen viel Erklärung/Beispiele auf verschiedenen init-Optionen und die „richtigen geben sollte "Weg, Dinge zu tun.


EDIT: Während Sie eine Bequemlichkeit initializer auf benutzerdefinierte Ansicht-Controller verwenden können, jeder ist zu Recht darauf hingewiesen, dass Sie keine benutzerdefinierten initializers verwenden können, wenn aus dem Storyboard oder durch ein Storyboard segue initialisiert.

Wenn Ihre Schnittstelle im Storyboard eingerichtet ist und Sie den Controller vollständig programmgesteuert erstellen, ist ein einfacher Initialisierer wahrscheinlich der einfachste Weg, um das zu tun, was Sie zu tun versuchen, da Sie sich nicht damit befassen müssen die benötigte init mit dem NSCoder (was ich immer noch nicht wirklich verstehe).

Wenn Sie jedoch Ihren View-Controller über das Storyboard erhalten, müssen Sie @Caleb Kleveter's answer folgen und den View-Controller in die gewünschte Unterklasse umwandeln und dann die Eigenschaft manuell festlegen.

+4

Aufenthalt golden ponyboy – thexande

11

Wie in einer der obigen Antworten angegeben, können Sie nicht beide und benutzerdefinierte Init-Methode und Storyboard verwenden. Sie können jedoch immer noch eine statische Methode verwenden, um ViewController aus einem Storyboard zu instanziieren und zusätzliche Einstellungen darauf vorzunehmen. Es wird wie folgt aussehen:

class MemeDetailVC : UIViewController { 

    var meme : Meme! 

    static func makeMemeDetailVC(meme: Meme) -> MemeDetailVC { 
     let newViewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("IdentifierOfYouViewController") as! MemeDetailVC 

     newViewController.meme = meme 

     return newViewController 
    } 
} 

nicht vergessen IdentifierOfYouViewController als View-Controller-Kennung in Ihrem Storyboard zu spezifizieren. Verwenden Sie auch nicht meme var in Ihrer ViewDidLoad, da es an diesem Punkt noch nicht initialisiert ist. Sie müssen möglicherweise auch den Namen des Storyboards im obigen Code ändern.

+0

Nach MemeDetailVC initialisiert wird, wird die Eigenschaft „meme“ existiert. Dann kommt die Abhängigkeitsinjektion, um den Wert "meme" zuzuweisen. Zu diesem Zeitpunkt wurden loadView() und viewDidLoad nicht aufgerufen. Diese beiden Methoden werden erhalten nach MemeDetailVC hinzufügen/Push-to-view-Hierarchie genannt. –

2

Wie @Caleb Kleveter darauf hingewiesen hat, können wir beim Initialisieren von einem Storyboard keinen benutzerdefinierten Initialisierer verwenden.

Aber wir Problem durch die Verwendung Fabrik/Klassenmethode lösen können, die Objekt-View-Controller von Storyboard instanziiert und View-Controller-Objekt zurück. Ich denke, das eine ziemlich coole Art und Weise ist.

Hinweis: Dies ist keine exakte Antwort auf Frage eher eine Problemumgehung, um das Problem zu lösen.

Make Klassenmethode, in MemeDetailVC Klasse wie folgt:

// Considering your view controller resides in Main.storyboard and it's identifier is set to "MemeDetailVC" 
class func `init`(meme: Meme) -> MemeDetailVC { 
    let storyboard = UIStoryboard(name: "Main", bundle: nil) 
    let vc = storyboard.instantiateViewController(withIdentifier: "MemeDetailVC") as? MemeDetailVC 
    vc.meme = meme 
    return vc 
} 

Verbrauch:

let memeDetailVC = MemeDetailVC.init(meme: Meme()) 
Verwandte Themen