简体   繁体   English

将 swift 中的闭包作为参数传递给 function 中的选择器使用

[英]Passing closure in swift as parameter to be used by selector in function

I am trying to create a generic button creation function into which I pass a closure that represents the action that results as a result of clicking on the button.我正在尝试创建一个通用按钮创建 function,我将一个闭包传递到其中,该闭包表示单击按钮后产生的操作。 My code is below.我的代码如下。 However, I get the following error: Argument of #selector cannot refer to property.但是,我收到以下错误:#selector 的参数无法引用属性。 Any suggestions for a workaround?有什么解决方法的建议吗? I don't want to write separate functions for which everything else is the same except for the target action.我不想编写单独的函数,除了目标操作之外,其他所有内容都相同。

 func myButton(textColor tColor:UIColor , title:String, 
                 _ buttonFcn: (UIButton) -> Void,   
                 titleSize:CGFloat=30) -> UIButton {
    let newButton = UIButton(type: .System)
    let bgColor = UIColor(red:204/255, green:204/255, blue:204/255, alpha:1.0)
    newButton.backgroundColor = bgColor

    newButton.setTitle(title, forState: .Normal)
    newButton.setTitleColor(tColor, forState: .Normal)

    newButton.titleLabel?.font = newButton.titleLabel?.font.fontWithSize(titleSize)


    newButton.addTarget(self,  action:#selector(buttonFcn),
                        forControlEvents: 
                           UIControlEvents.TouchUpInside)

    return newButton
}

The problem is that the target-action mechanism is an Objective-C mechanism, and therefore is predicated on the notion that the action selector is a method of an object . 问题在于目标动作机制是一种Objective-C机制,因此基于动作选择器是一种对象 方法的概念来进行判断。 You need, therefore, to have some NSObject-based object that has this function as a method , and which can then serve as the target. 因此,您需要具有一些基于NSObject的对象 ,该对象具有将此功能用作方法 ,然后可以用作目标。

Thus, if what differs in every case is the target and the action, what you need to pass is a reference to the target along with the selector string . 因此,如果目标和操作在每种情况下都不同,那么您需要传递的是对目标的引用以及选择器字符串 Swift will squawk at this, but if you know how to form a selector string correctly you can certainly get away with it; Swift会对此之以鼻,但是,如果您知道如何正确形成选择器字符串,那么您当然可以摆脱它; you just won't be able to use the #selector syntax, and so you will risk crashing if you form the selector string incorrectly. 您将无法使用#selector语法,因此,如果输入格式不正确,可能会导致崩溃。 But it's the kind of thing we used to do all the time in the old Objective-C days, so go right ahead if that's your aim. 但这是我们在过去的Objective-C时代一直做的事情,因此,如果这是您的目标,那就继续吧。

Totally artificial but working example: 完全人为但可行的示例:

func buttonMaker(target:NSObject, selectorString:String) -> UIButton {
    let b = UIButton(type:.system)
    b.setTitle("Testing", for: .normal)
    b.addTarget(target, action: Selector(selectorString), for: .touchUpInside)
    b.sizeToFit()
    return b
}

And here's how to call it from a view controller: 这是从视图控制器调用它的方法:

func doButton(_ sender:Any) {
    print("ha!")
}

override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.
    let b = buttonMaker(target:self, selectorString:"doButton:")
    b.frame.origin = CGPoint(x:100, y:100)
    self.view.addSubview(b)
}

And when we tap the button, we don't crash (rather, we print "ha"), because I know how to make selector strings correctly. 而且当我们点击按钮时,我们不会崩溃(而是打印“ ha”),因为我知道如何正确制作选择器字符串。 But, as you can see, to accomplish this I had to give up the use of #selector altogether, so safety is out the window. 但是,正如您所看到的,要实现此目的,我必须完全放弃使用#selector ,因此安全性不在考虑之列。 If I had written my selector string incorrectly — for instance, if I had spelled it wrong, or omitted the colon — we'd have crashed on the button tap, just like we used to all the time before Swift #selector and Objective-C @selector were invented. 如果我没有正确输入选择器字符串(例如,如果我拼写错误或省略了冒号),我们将在按钮点击时崩溃,就像我们在Swift #selector和Objective-C之前一直都一样@selector被发明了。

If your deployment target is iOS 14 or later, you can use the addAction method instead of addTarget .如果您的部署目标是 iOS 14 或更高版本,您可以使用addAction方法而不是addTarget The addAction method lets you use a closure instead of a selector: addAction方法允许您使用闭包而不是选择器:

func myButton(
    textColor: UIColor,
    title: String,
    titleSize: CGFloat = 30,
    _ handler: @escaping (UIButton) -> Void
) -> UIButton {
    let button = UIButton(type: .system)
    button.backgroundColor = UIColor(red: 204/255, green: 204/255, blue: 204/255, alpha: 1.0)
    button.setTitle(title, for: .normal)
    button.setTitleColor(textColor, for: .normal)
    button.titleLabel?.font = button.titleLabel?.font.withSize(titleSize)

    let action = UIAction { action in
        guard let button = action.sender as? UIButton else { return }
        handler(button)
    }
    button.addAction(action, for: .touchUpInside)

    return button
}

iOS 14 was released on 2020-09-16 and supports iPhone 6S and later devices. iOS 14于2020-09-16发布,支持iPhone 6S及之后的设备。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM