2015-06-16 2 views
11

Ich frage mich, was das Swift-Äquivalent beim Aufruf einer Methode auf id ist, in der die Verfügbarkeit der Methode zur Laufzeit festgestellt wird. Insbesondere freue ich dieses Muster in Swift zu tun:Was ist das schnelle Äquivalent zum Festlegen von Eigenschaften für ID?

-(IBAction) handleEvent:(id) sender { 
    BOOL didDisable = NO; 
    if([sender respondsToSelector:@selector(setEnabled:)]) { 
     [sender setEnabled:NO]; 
     didDisable = YES; 
    } 
    [self doSomethingAsyncWithCompletionHandler:^{ 
     if(didDisable) { 
      [sender setEnabled:YES]; 
     } 
    }]; 
} 

Das größte Problem ist, dass setEnabled: in Swift als Eigenschaft importiert wird (zB UIBarItem) und keines der folgenden Konstrukte kompilieren

func handleEvent(sender: AnyObject) { 
    // Error: AnyObject does not have a member named "enabled" 
    sender.enabled? = false 

    // Error: (BooleanLiteralCompatible) -> _ is not identical to Bool 
    sender.setEnabled?(false) 
} 
+1

Wenn möglich, implementieren Sie diese Funktionalität über Protokolle. Sie könnten dann den 'ansagesToSelector' Check mit einem' if-let' ändern, wie 'if toggles = sender as as? Umschaltbar (unter der Annahme, dass das Protokoll mit der Eigenschaft 'enabled' umschaltbar ist). – dcestari

+0

Nicht sicher, aber vielleicht AnyObject? – kostek

+0

http://roadfiresoftware.com/2014/07/swifts-var-is-not-objective-cs-id/ –

Antwort

6

In Swift 2.0 Beta 4, deine Gebete werden beantwortet; Dieser Code wird legal:

@IBAction 
func handleEvent(sender: AnyObject) { 
    if sender.respondsToSelector("setHidden:") { 
     sender.performSelector("setHidden:", withObject: true) 
    } 
} 
+0

Seltsamerweise kann ich es nicht zu _do_ irgendetwas für 'setEnabled:' (es kompiliert, aber die Schaltfläche deaktiviert nicht). Dies könnte jedoch ein vorübergehender Fehler sein. da es für "hidden" funktioniert, sollten wir davon ausgehen, dass es für 'enabled' funktionieren soll. – matt

+1

Ich habe einen Fehler beim 'performSelector (" setEnabled: ")' Problem gemacht. Es stürzt nicht ab, aber es tut auch nichts. – matt

+0

Ich konnte es mit 'setEnabled:', aber auf eine seltsame Art und Weise ... '.performSelector (" setEnabled: ")' immer setzt den Wert auf ** wahr ** und '.performSelector (" setEnabled: ", withObject: nil)" setzt immer aktiviert auf ** false **, unabhängig vom Wert von 'withObject:'. Ich denke, es verdient einen Fehler zu stellen ... –

24

Sie können es genau so machen, wie Sie es vorher getan haben: indem Sie respondsToSelector: anrufen. Tatsächlich das ist genau das, was Ihr vorgeschlagene Ausdruck tut:

sender.setEnabled?(false) 

Dieser Ausdruck eigentlich eine Abkürzung ist - es nennt respondsToSelector: zuerst, und dann ruft setEnabled: nur, wenn der respondsToSelector: Test besteht. Leider, wie Sie sagen, können Sie diesen Code nicht kompilieren. Das ist jedoch nur eine Eigenart von Swifts bekanntem Repertoire an verfügbaren Methoden. Tatsache ist, dass es zwar ein wenig schwierig ist, es zum Kompilieren zu bringen, aber es kann auch so sein, wie Sie es erwarten würden.

Allerdings werde ich nicht erklären wie, um es zu kompilieren, weil ich diese Art von Tricks nicht ermutigen will. Diese Art der dynamischen Nachrichtenübertragung wird in Swift nicht empfohlen. Im Allgemeinen werden dynamische Swift-Tricks wie Schlüsselwertcodierung, Introspektion usw. in Swift nicht benötigt und entsprechen nicht Swifts typischem Tipping-Ansatz. Es wäre besser, Dinge auf schnelle Art zu tun, indem man optional auf etwas wirft, von dem Sie Grund haben zu glauben, dass dieses Ding sein könnte und das eine enabled Eigenschaft hat. Zum Beispiel:

@IBAction func doButton(sender: AnyObject) { 
    switch sender { 
    case let c as UIControl: c.enabled = false 
    case let b as UIBarItem: b.enabled = false 
    default:break 
    } 
} 

Oder:

@IBAction func doButton(sender: AnyObject) { 
    (sender as? UIControl)?.enabled = false 
    (sender as? UIBarItem)?.enabled = false 
} 
+2

Daumen hoch für nicht ermutigend stinkender Code –

+0

Sie sagen mir instanceof ist weniger stinkend? –

+0

"... dynamisches Messaging wird in Swift abgeraten ...", während der Rest von Cocoa es stark nutzt. Ich werde bei Objective-C bleiben, wenn dies der Fall ist. – adib

2

Wenn Sie die respondsToSelector: Methode zu vermeiden, mit wollen könnten Sie ein Protokoll stattdessen definieren. Erweitern Sie dann die zu verwendenden Klassen, die bereits der Definition dieses Protokolls entsprechen (aktiviert) und definieren Sie die Funktion mit einer generischen Variablen, die Ihrem Protokoll entspricht.

protocol Enablable{ 
    var enabled:Bool { get set } 
} 

extension UIButton  : Enablable {} 
extension UIBarButtonItem : Enablable {} 

//.... 

func handleEvent<T:Enablable>(var sender: T) { 
    sender.enabled = false 
} 

Wenn Sie es mit einer IBAction Methode ein wenig von einer Arbeit um erforderlich ist, verwenden müssen, da Sie nicht Generika direkt auf sie verwenden können.

@IBAction func handleEventPressed(sender:AnyObject){ 
    handleEvent(sender); 
} 

Wir brauchen auch eine passende generische Funktion ohne Enablable Konformität, so dass wir ohne zu wissen, ob handle oder nicht Absender anrufen kann, ist Enablable. Zum Glück ist der Compiler schlau genug, um herauszufinden, welche der beiden generischen Funktionen zu verwenden ist.

func handleEvent<T>(var sender: T) { 
    //Do Nothing case if T does not conform to Enablable 
} 
1

Als Abhilfe/Alternative, Sie Schlüsselwert Coding verwenden:

@IBAction func handler(sender: AnyObject) { 

    if sender.respondsToSelector("setEnabled:") { 
     sender.setValue(false, forKey:"enabled") 
    } 
} 

Dies funktioniert sowohl mit Swift 1.2 (Xcode 6.4) und Swift 2.0 (Xcode 7 Beta).

Verwandte Themen