在Swift中将函数作为参数传递

58

我已经有一个符合我的预期的函数,运行在iOS 8中:

func showConfirmBox(msg:String, title:String,
    firstBtnStr:String,
    secondBtnStr:String,
    caller:UIViewController) {
        let userPopUp = UIAlertController(title:title,
            message:msg, preferredStyle:UIAlertControllerStyle.Alert)
        userPopUp.addAction(UIAlertAction(title:firstBtnStr, style:UIAlertActionStyle.Default,
            handler:{action in}))
        userPopUp.addAction(UIAlertAction(title:secondBtnStr, style:UIAlertActionStyle.Default,
            handler:{action in}))
        caller.presentViewController(userPopUp, animated: true, completion: nil)
}
我想做出以下类似的东西,以便在其中将要触摸其中一个按钮时作为参数传递要执行的方法:

我想做出以下类似的东西,以便在其中将要触摸其中一个按钮时作为参数传递要执行的方法:

func showConfirmBox(msg:String, title:String,
    firstBtnStr:String, firstSelector:Selector,
    secondBtnStr:String, secondSelector:Selector,
    caller:UIViewController) {
        let userPopUp = UIAlertController(title:title,
            message:msg, preferredStyle:UIAlertControllerStyle.Alert)
        userPopUp.addAction(UIAlertAction(title:firstBtnStr, style:UIAlertActionStyle.Default,
            handler:{action in caller.firstSelector()}))
        userPopUp.addAction(UIAlertAction(title:secondBtnStr, style:UIAlertActionStyle.Default,
            handler:{action in caller.secondSelector()}))
        caller.presentViewController(userPopUp, animated: true, completion: nil)
}

显然,我在使用firstSelector和secondSelector方面没有做正确的事情,因为到目前为止我尝试过的方法都没有起作用。我想我没有使用我想要的正确语法,但我确信可以实现我想做的事情。有什么好的方法来正确地做到这一点吗?


你说的“没有工作”是什么意思?请提供更具体的信息。 - l'L'l
我的意思是,我从编译器那里得到错误消息。如果有用的话,我可以包含它们。除此之外,我想我的第二个函数的语法只是有问题。 - Michel
我正在尝试自己找到其他方法(例如使用泛型),但目前仍然没有成功。 - Michel
1
现在它可以了,我弄明白了。只需要使用函数名称而不需要引号。谢谢! - Michel
4个回答

102

你问题的简短回答是闭包

闭包的默认语法为() -> ()

你可以直接提到方法定义,而不是选择器。

func showConfirmBox(msg:String, title:String,
    firstBtnStr:String, firstSelector:(sampleParameter: String) -> returntype,
    secondBtnStr:String, secondSelector:() -> returntype,
    caller:UIViewController) {
    //Your Code
}

但是使用这个会导致可读性问题,所以我建议你使用typeAlias

typealias MethodHandler1 = (sampleParameter : String)  -> Void
typealias MethodHandler2 = ()  -> Void

func showConfirmBox(msg:String, title:String,
                    firstBtnStr:String, firstSelector:MethodHandler1,
                    secondBtnStr:String, secondSelector:MethodHandler2) {

    // After any asynchronous call
    // Call any of your closures based on your logic like this
    firstSelector("FirstButtonString")
    secondSelector()
}

你可以这样调用你的方法

func anyMethod() {
   //Some other logic 

   showConfirmBox(msg: "msg", title: "title", firstBtnStr: "btnString", 
         firstSelector: { (firstSelectorString) in
              print(firstSelectorString) //this prints FirstButtonString
         }, 
         secondBtnStr: "btnstring") { 
           //Invocation comes here after secondSelector is called

         }
}

5
自Swift 3以来,您无法为函数类型分配参数标签。因此,MethodHandler1应该像这样编写:"typealias MethodHandler1 = (String) -> Void"。参考来源: https://dev59.com/k1kS5IYBdhLWcg3wn4C-#39717607 - Oded Ben Dov

17

万一有其他人也遇到这个问题。我在为一个项目构建全局警报实用程序时,制定了Swift 5.1的更新简单解决方案。

Swift 5.1

带有闭包的函数:

func showSheetAlertWithOneAction(messageText: String, dismissButtonText: String, actionButtonText : String, presentingView : NSWindow, actionButtonClosure: @escaping () -> Void) {
        let alert = NSAlert()
        alert.messageText = messageText
        alert.addButton(withTitle: actionButtonText)
        alert.addButton(withTitle: dismissButtonText)
        alert.beginSheetModal(for: presentingView) { (response) in
            if response == .alertFirstButtonReturn {
                actionButtonClosure()
            }
        }
    }

函数被调用:

showSheetAlertWithOneAction(messageText: "Here's a message", dismissButtonText: "Nope", actionButtonText: "Okay", presentingView: self.view.window!) {
                                            someFunction()
                                }

11

在got2jam的回答基础上补充...

如果您正在使用UIAlertController

显示带有闭包的警报的通用函数:

func showAlertAction(title: String, message: String, actionClosure: @escaping () -> Void){
  let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
  alertController.addAction(UIAlertAction(title: "Ok", style: UIAlertAction.Style.default, handler: {(action: UIAlertAction!) in actionClosure()}))
  self.present(alertController, animated: true, completion: nil)
}

现在你可以这样调用它:

showAlertAction(title: "This is the title", message: "This is the message") {
   self.close()
}
在这种情况下,close是要执行的特定UIAlertAction。
func close(){
  dismiss(animated: true, completion: nil)
}

我有一个带有两个UITextField的UIAlertController(由不同的UIViewController调用),这使得一切变得更加复杂。为了将输入数据返回给调用函数,可以使用actionClosure: @escaping (_ input1:String, _input2:String)将其添加到闭包中,最后通过actionClosure("Hello","World")传递它。如果您想检查输入的有效性,则可以在inactionClosure()之间进行检查。 - Neph

0

我根据各种网站示例编写了这个程序。以下是我调用该程序的方式...

@IBAction func buttonClick(_ sender: Any) {
    SS_Alert.createAlert(parmTitle: "Choose", parmMessage: "Please select Yes or No", parmOptions: ["Yes","No","Cancel"], parmFunctions: [testYes, testNo, nil])
}

func testYes() {
    print("yes")
}

func testNo() {
    print("no")
}

您可以传递按钮选项和在选择按钮时执行的函数。花了一点时间才弄清如何将函数作为参数传递,但现在似乎运行良好。我尝试使用循环动态添加按钮时遇到了一个奇怪的问题,最后放弃并使用了switch/case。我包括了我尝试使用的循环代码,如果有人能够找出我做错了什么,请告诉我。谢谢。

https://github.com/blakeguitar/iOS/blob/0e243d13cb2decd6e1dbe134a8a046c2caed3876/SS_Alert.swift


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接