Ответ 1
В Swift 2.0 beta 4 ваши молитвы отвечают; этот код становится законным:
@IBAction
func handleEvent(sender: AnyObject) {
if sender.respondsToSelector("setHidden:") {
sender.performSelector("setHidden:", withObject: true)
}
}
Интересно, что эквивалент Swift при вызове метода на id
, в котором определяется доступность метода во время выполнения. В частности, я хочу сделать этот шаблон в Swift:
-(IBAction) handleEvent:(id) sender {
BOOL didDisable = NO;
if([sender respondsToSelector:@selector(setEnabled:)]) {
[sender setEnabled:NO];
didDisable = YES;
}
[self doSomethingAsyncWithCompletionHandler:^{
if(didDisable) {
[sender setEnabled:YES];
}
}];
}
Самая большая проблема заключается в том, что setEnabled:
импортируется в Swift как свойство (например, UIBarItem
), и ни одна из следующих конструкций не компилирует
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)
}
В Swift 2.0 beta 4 ваши молитвы отвечают; этот код становится законным:
@IBAction
func handleEvent(sender: AnyObject) {
if sender.respondsToSelector("setHidden:") {
sender.performSelector("setHidden:", withObject: true)
}
}
Фактически вы можете сделать это точно так же, как и раньше: звоните respondsToSelector:
. В самом деле, это именно то, что делает ваше предлагаемое выражение:
sender.setEnabled?(false)
Это выражение на самом деле является сокращением - сначала он вызывает respondsToSelector:
, а затем вызывает setEnabled:
, только если проходит тест respondsToSelector:
. К сожалению, как вы говорите, вы не можете получить этот код для компиляции. Это, однако, просто причуда Свифта, известного репертуара доступных методов. Дело в том, что, хотя немного сложно сделать это для компиляции, это можно сделать - и как только вы его скомпилируете, он ведет себя так, как вы ожидали.
Однако я не буду объяснять, как это сделать, потому что я не хочу поощрять подобные трюки. Такой тип динамических сообщений не рекомендуется в Swift. В общем, в Swift не нужны динамические сообщения, такие как кодирование с ключом, интроспекция и т.д., И не согласуются с подходом Swift с сильной типизацией. Было бы лучше сделать что-то быстро, добавив опцию к чему-то, что у вас есть основания полагать, что это может быть и что имеет свойство enabled
. Например:
@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
}
}
Или:
@IBAction func doButton(sender: AnyObject) {
(sender as? UIControl)?.enabled = false
(sender as? UIBarItem)?.enabled = false
}
Если вы хотите избежать использования метода respondsToSelector:
, вы можете определить протокол вместо этого. Затем расширьте классы, которые вы хотите использовать, которые уже соответствуют определению этого протокола (включено) и определяют функцию с общей переменной, соответствующей вашему протоколу.
protocol Enablable{
var enabled:Bool { get set }
}
extension UIButton : Enablable {}
extension UIBarButtonItem : Enablable {}
//....
func handleEvent<T:Enablable>(var sender: T) {
sender.enabled = false
}
Если вам нужно использовать его с помощью метода IBAction, вам потребуется немного работать, потому что вы не можете использовать генерики прямо на них.
@IBAction func handleEventPressed(sender:AnyObject){
handleEvent(sender);
}
Нам также нужна соответствующая общая функция без соответствия Enablable, так что мы можем вызвать handleEvent, не зная, что кто-либо, или не отправитель, является Enablable. К счастью, компилятор достаточно умен, чтобы выяснить, какую из двух общих функций использовать.
func handleEvent<T>(var sender: T) {
//Do Nothing case if T does not conform to Enablable
}
В качестве обходного пути/альтернативы вы можете использовать кодирование с ключом:
@IBAction func handler(sender: AnyObject) {
if sender.respondsToSelector("setEnabled:") {
sender.setValue(false, forKey:"enabled")
}
}
Это работает как с Swift 1.2 (Xcode 6.4), так и с Swift 2.0 (Xcode 7 beta).