2015-07-01 10 views
8

Ich bin in der Lage, eine Instanz einer UIView-Unterklasse aus einem Nib herauszuziehen.Mit 'self' in Klassenerweiterungsfunktionen in Swift

Ich möchte MyCustomView.instantiateFromNib() aufrufen und eine Instanz von MyCustomView haben. Ich bin fast bereit, den funktionierenden Objective-C-Code, den ich über den Bridging-Header habe, einfach zu portieren, aber ich dachte, ich würde zuerst den idiomatischen Ansatz ausprobieren. Das war vor zwei Stunden.

extension UIView { 
    class func instantiateFromNib() -> Self? { 

     let topLevelObjects = NSBundle.mainBundle().loadNibNamed("CustomViews", owner: nil, options: nil) 

     for topLevelObject in topLevelObjects { 
      if (topLevelObject is self) { 
       return topLevelObject 
      } 
     } 

     return nil 
    } 
} 

Jetzt ist if (topLevelObject is self) { falsch, weil "Expected Art nach 'ist'". Was ich danach versucht habe, zeigt viel über das, was ich über das Swift-System nicht verstehe.

  • if (topLevelObject is Self) {
  • if (topLevelObject is self.dynamicType) {
  • if (topLevelObject is self.self) {
  • Eine Million andere Variationen, die not even wrong sind.

Jeder Einblick wird geschätzt.

Antwort

14

Mit dem Ansatz von How can I create instances of managed object subclasses in a NSManagedObject Swift extension? können Sie eine allgemeine Hilfsmethode definieren, die die Art des self vom rufenden Kontext folgert:

extension UIView { 

    class func instantiateFromNib() -> Self? { 
     return instantiateFromNibHelper() 
    } 

    private class func instantiateFromNibHelper<T>() -> T? { 
     let topLevelObjects = NSBundle.mainBundle().loadNibNamed("CustomViews", owner: nil, options: nil) 

     for topLevelObject in topLevelObjects { 
      if let object = topLevelObject as? T { 
       return object 
      } 
     } 
     return nil 
    } 
} 

Dies kompiliert und arbeitet wie in meinem kurzen Test erwartet. Wenn MyCustomView Ihre UIView Unterklasse ist dann

if let customView = MyCustomView.instantiateFromNib() { 
    // `customView` is a `MyCustomView` 
    // ... 
} else { 
    // Not found in Nib file 
} 

gibt Ihnen eine Instanz von MyCustomView, und der Typ ist automatisch gefolgert.


Update für Swift 3:

extension UIView { 

    class func instantiateFromNib() -> Self? { 
     return instantiateFromNibHelper() 
    } 

    private class func instantiateFromNibHelper<T>() -> T? { 
     if let topLevelObjects = Bundle.main.loadNibNamed("CustomViews", owner: nil, options: nil) { 
      for topLevelObject in topLevelObjects { 
       if let object = topLevelObject as? T { 
        return object 
       } 
      } 
     } 
     return nil 
    } 
} 
+1

Fantastisch!Ich hatte meinen Zeh in generische Methoden getaucht, um zu versuchen, dies zu lösen und scheiterte kläglich. Vielen Dank! – rob5408

+0

Ich fand einen seltsamen Fehler mit diesem, wo es auf dem Simulator und auf dem Gerät funktioniert, wenn es über die Entwicklungsbereitstellung getan wird, aber es verwendet nicht die Klasse der Ansicht, die innerhalb der Nib angegeben ist. Ich bekomme Abstürze, die melden, dass eine andere Ansicht mit einem anderen Typ zurückgegeben wird, und ich behandle sie als den erwarteten Typ. Seltsam. – rob5408

+0

Das Flag für die Optimierungsebene für Release-Builds wurde auf None gesetzt. Jetzt muss ich herausfinden, wie ich es richtig beheben kann. – rob5408

2

Ich glaube, der bedingte Ausdruck Sie suchen ist topLevelObject.dynamicType == self

diese Kombination mit unsafeBitCast(which, by Apple's own documentation, "Breaks the guarantees of Swift's type system"), können wir mit Nachdruck gesenkten topLevelObject-self ist der Typ. Diese sollte sicher sein, weil wir bereits dafür gesorgt, dass topLevelObject ist vom gleichen Typ wie self

Dieser Weg ist, um die Hilfsmethode unter Verwendung von Generika zu erhalten, den Martin R beschrieben.

extension UIView { 
    class func instantiateFromNib() -> Self? { 

     let bundle = NSBundle.mainBundle().loadNibNamed("CustomViews", owner: nil, options: nil) 

     for topLevelObject in topLevelObjects { 
      if topLevelObject.dynamicType == self { 
       return unsafeBitCast(topLevelObject, self) 
      } 
     } 
     return nil 
    } 
} 

Beachten Sie, dass Apple sagt, auch in ihrer Dokumentation für unsafeBitCast:

Es gibt fast immer einen besseren Weg, etwas zu tun.

Also Vorsicht!

Verwandte Themen