メソッドのselector
引数にクロージャープロパティを参照させたいのですが、どちらも同じスコープに存在します。例えば、
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
引数にクロージャーを設定することは可能ですか?
@ 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)
直接ではありませんが、いくつかの回避策が可能です。次の例を見てください。
/// Target-Action helper.
final class Action: NSObject {
private let _action: () -> ()
init(action: @escaping () -> ()) {
_action = action
super.init()
}
@objc func action() {
_action()
}
}
let action1 = Action { print("action1 triggered") }
let button = UIButton()
button.addTarget(action1, action: #selector(action1.action), forControlEvents: .TouchUpInside)
少なくとも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!")
})
今では可能です。 Swift 4.でブロックベースのセレクターの要点を作成しました。
https://Gist.github.com/cprovatas/98ff940140c8744c4d1f3bcce7ba454
使用法:
UIButton().addTarget(Selector, action: Selector { debugPrint("my code here") }, for: .touchUpInside)
UIControl、UIButton、UIRefreshControl、UIGestureRecognizer、およびUIBarButtonItemをサポートするActionClosurableを使用できます。 https://github.com/takasek/ActionClosurable
UIBarButtonItemの以下の表示例
// UIBarButtonItem
let barButtonItem = UIBarButtonItem(title: "title", style: .plain) { _ in
print("barButtonItem title")
}
いいえ、#selectorはObjective-Cメソッドを指します。
ただし、NSTimerに拡張機能を追加すると、ターゲットとセレクターではなく、クロージャーを使用してスケジュールタイマーを作成できます。
ブロックのスコープを関数ではなくクラススコープに変更し、そこでクロージャーへの参照を保持する場合。
そのクロージャーを関数で呼び出すことができます。クラスで。そのため、そのクロージャーをセレクターとして呼び出すことができます。
このようなもの:
class Test: NSObject {
let backToOriginalBackground = {
}
func backgroundChange() {
NSTimer.scheduledTimerWithTimeInterval(0.5, target: self, selector: #selector(test), userInfo: nil, repeats: false)
}
func test() {
self.backToOriginalBackground()
}
}
したがって、selector
をSwiftのような方法で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)
私の解決策は、次のようなクラスブロック変数を作成することでした。
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)
}