2016-05-02 47 views
15

我想使我的方法的參數selector引用一個閉包屬性,它們都存在於相同的範圍內。例如,我可以讓#選擇器引用Swift中的閉包嗎?

func backgroundChange() { 
    self.view.backgroundColor = UIColor.blackColor() 
    self.view.alpha = 0.55 

    let backToOriginalBackground = { 
     self.view.backgroundColor = UIColor.whiteColor() 
     self.view.alpha = 1.0 
    } 

    NSTimer.scheduledTimerWithTimeInterval(0.5, target: self, selector: #selector(backToOriginalBackground), userInfo: nil, repeats: false) 
} 

但是,這顯示錯誤:Argument of #selector cannot refer to a property

當然,我可以定義一個新的單獨的方法,並將閉包的實現移動到它,但我想保持節約這種小實現。

是否可以將關閉設置爲#selector參數?

回答

4

正如@ gnasher729指出,這是不可能的,因爲選擇僅僅是方法的名稱,而不是方法本身。在一般情況下,我會使用dispatch_after在這裏,但在這個特殊的情況下,更好的工具IMO是UIView.animateWithDuration,因爲它是功能是什麼,可以而且很容易調整的過渡:

UIView.animateWithDuration(0, delay: 0.5, options: [], animations: { 
    self.view.backgroundColor = UIColor.whiteColor() 
    self.view.alpha = 1.0 
}, completion: nil) 
+0

是啊!而已。我們在思考語法,而不是OP面臨的問題,這是更好的解決方案。 –

+0

請在下面看到我的帖子。這是可能的w /一些objc運行時的魔法:) –

1

不,#選擇器是指Objective-C方法。

儘管如此,您還是可以做得更好:向NSTimer添加一個擴展,它可以讓您創建一個不帶目標和選擇器的預定計時器,但帶有閉包。

+0

我會感興趣的是看到什麼樣子。這在很多方面都會有用。 – ryantxr

+0

創建這個目標有點棘手,因爲'NSTimer'保留了它的'目標',但絕對有可能。不過最好使用'dispatch_after'。 – Sulthan

18

不是直接的,但一些解決方法是可能的。看看下面的例子。

/// Target-Action helper. 
final class Action: NSObject { 

    private let _action:() ->() 

    init(action:() ->()) { 
     _action = action 
     super.init() 
    } 

    func action() { 
     _action() 
    } 

} 

let action1 = Action { print("action1 triggered") } 

let button = UIButton() 
button.addTarget(action1, action: #selector(action1.action), forControlEvents: .TouchUpInside) 
0

如果將塊的作用域更改爲類作用域而非函數,並在此處保留對閉包的引用。

您可以使用函數調用該閉包。在課堂裏。這樣你就可以調用這個閉包作爲選擇器。

事情是這樣的:

class Test: NSObject { 
    let backToOriginalBackground = { 

    } 
    func backgroundChange() { 
     NSTimer.scheduledTimerWithTimeInterval(0.5, target: self, selector: #selector(test), userInfo: nil, repeats: false) 
    } 

    func test() { 
     self.backToOriginalBackground() 
    } 
} 
0

我的解決方案是創建一個類塊變量,如:

let completionBlock:() ->() = nil 

創建調用該completionBlock的方法:

func completed(){ 
    self.completionBlock!() 
} 

而且在一邊在這裏我想把我的選擇像塊我所做的:

func myFunc(){ 
    self.completionBlock = {//what I want to be done} 
    NSTimer.scheduledTimerWithTimeInterval(0.5, target: self, selector: #selector(Myclass.completed), userInfo: nil, repeats: false) 
} 
0

所以我的答案具有selector以同樣方式迅速被分配到一個closure類似於一些已經是答案,但我認爲我會分享一個真實生活的例子,說明如何在UIViewController擴展中做到這一點。

fileprivate class BarButtonItem: UIBarButtonItem { 
    var actionCallback: (() -> Void)? 
    func buttonAction() { 
    actionCallback?() 
    } 
} 

fileprivate extension Selector { 
    static let onBarButtonAction = #selector(BarButtonItem.buttonAction) 
} 

extension UIViewController { 
    func createBarButtonItem(title: String, action: @escaping() -> Void) -> UIBarButtonItem { 
    let button = BarButtonItem(title: title, style: .plain, target nil, action: nil) 
    button.actionCallback = action 
    button.action = .onBarButtonAction 
    return button 
    } 
} 

// Example where button is inside a method of a UIViewController 
// and added to the navigationItem of the UINavigationController 

let button = createBarButtonItem(title: "Done"){ 
    print("Do something when done") 
} 

navigationItem.setLeftbarButtonItems([button], animated: false) 
1

這個我試過了的UIBarButtonItem至少:

private var actionKey: Void? 

extension UIBarButtonItem { 

    private var _action:() ->() { 
     get { 
      return objc_getAssociatedObject(self, &actionKey) as!() ->() 
     } 
     set { 
      objc_setAssociatedObject(self, &actionKey, newValue, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC) 
     } 
    } 

    convenience init(title: String?, style: UIBarButtonItemStyle, action: @escaping() ->()) { 
     self.init(title: title, style: style, target: nil, action: #selector(pressed)) 
     self.target = self 
     self._action = action 
    } 

    @objc private func pressed(sender: UIBarButtonItem) { 
     _action() 
    } 

} 

然後,你可以這樣做:

navigationItem.leftBarButtonItem = UIBarButtonItem(title: "Test", style: .plain, action: { 
    print("Hello World!") 
}) 
+0

這是非常方便,很高興保持作爲一個Swift實現。謝謝 – dmclean

相關問題