【问题标题】:Attach parameter to button.addTarget action in Swift将参数附加到 Swift 中的 button.addTarget 操作
【发布时间】:2014-09-08 23:31:42
【问题描述】:

我正在尝试向 buttonClicked 操作传递一个额外的参数,但无法确定 Swift 中的语法。

button.addTarget(self, action: "buttonClicked:", forControlEvents: UIControlEvents.TouchUpInside)

我的任何 buttonClicked 方法:

func buttonClicked(sender:UIButton)
{
    println("hello")
}

有人有什么想法吗?

感谢您的帮助。

【问题讨论】:

  • 参考this link获取详细答案和最佳实践
  • 这个问题没有很好的答案,因为它已经描述了一个有问题的架构。您不需要额外的参数。需要 n 个附加参数是您的架构中的一个问题。
  • WARNING TO INTERNET: please check out my answer at the bottom

标签: swift button uibutton


【解决方案1】:

您不能在addTarget: 中传递自定义参数。另一种方法是设置按钮的tag 属性并根据标签进行工作。

button.tag = 5
button.addTarget(self, action: "buttonClicked:", 
    forControlEvents: UIControlEvents.TouchUpInside)

或者对于 Swift 2.2 及更高版本:

button.tag = 5
button.addTarget(self,action:#selector(buttonClicked),
    forControlEvents:.TouchUpInside)

现在根据tag属性做逻辑

@objc func buttonClicked(sender:UIButton)
{
    if(sender.tag == 5){

        var abc = "argOne" //Do something for tag 5
    }
    print("hello")
}

【讨论】:

  • 嗨,如果我想在 tableView 中获取 indexPath 或按钮单元格,可以吗?
  • 这里有一个提示:不要将方法设为私有。
  • 似乎它的类型只能是Int。没有别的了。
  • 如果我想同时传递行和节怎么办?
  • 只是让你知道,尽管有 118 人支持(并且还在计数),但这样做的方法是错误的
【解决方案2】:

如果你想向 buttonClicked 方法发送额外的参数,例如 indexPath 或 urlString,你可以继承 UIButton:

class SubclassedUIButton: UIButton {
    var indexPath: Int?
    var urlString: String?
}

确保将身份检查器中的按钮类更改为 subclassedUIButton。您可以使用sender.indexPathsender.urlString 访问buttonClicked 方法中的参数。

注意:如果您的按钮位于单元格内,您可以在 cellForRowAtIndexPath 方法(创建按钮的位置)中设置这些附加参数的值。

【讨论】:

  • 这是唯一对我有用的方法。但我很好奇,这是不是反模式?
  • 我认为这是最好的方法
【解决方案3】:

我很欣赏大家说使用标签,但实际上你需要扩展 UIButton 类并简单地在那里添加对象..

标签是解决这个问题的绝招。像这样扩展 UIButton(在 Swift 4 中)

import UIKit
class PassableUIButton: UIButton{
    var params: Dictionary<String, Any>
    override init(frame: CGRect) {
        self.params = [:]
        super.init(frame: frame)
    }

    required init?(coder aDecoder: NSCoder) {
        self.params = [:]
        super.init(coder: aDecoder)
    }
}

那么您的呼叫可能是呼叫(注意Selector(("webButtonTouched:")) 中的冒号“:”)

let webButton = PassableUIButton(frame: CGRect(x:310, y:40, width:40, height:40))
webButton.setTitle("Visit",for: .normal)
webButton.addTarget(self, action: #selector(YourViewController.webButtonTouched(_:)), for:.touchUpInside)
webButton.params["myvalue"] = "bob"

那么终于在这里全部捕捉到了

@IBAction func webButtonTouched(_ sender: PassableUIButton) {
    print(sender.params["myvalue"] ?? "")
}

您只需要这样做一次并在整个项目中使用它(您甚至可以让子类拥有一个通用的“对象”并将您喜欢的任何内容放入按钮中!)。或者使用上面的示例将无穷无尽的键/字符串参数放入按钮中。对于包含 url、确认消息方法等内容非常有用

顺便说一句,SO 社区意识到这一点很重要'没有被教导/错过object extensions的概念的要点

【讨论】:

  • 谁能评论这是否是正确的做法?
  • @Eric33187 这是一种正确的,而且更清晰的面向对象的方法。但是使用标签是一种惰性解决方案。他们都工作,但使用标签 - 气味
【解决方案4】:

对于 Swift 3.0,您可以使用以下方法

button.addTarget(self, action: #selector(YourViewController.YourMethodName(_:)), for:.touchUpInside)

func YourMethodName(_ sender : UIButton) {
    print(sender.tag)

}

【讨论】:

    【解决方案5】:

    斯威夫特 4.2

    结果:

    testButton.on(.touchUpInside) { (sender, event) in
        // You can use any reference initialized before the code block here
        // You can access self by adding [weak self] before (sender, event)
        // You can then either make self strong by using a guard statement or use a optional operator (?)
        print("user did press test button")
    }
    

    在文件UIButton+Events.swift 中,我为UIButton 创建了一个扩展方法,它将UIControl.Event 绑定到名为EventHandler 的完成处理程序:

    import UIKit
    
    fileprivate var bindedEvents: [UIButton:EventBinder] = [:]
    
    fileprivate class EventBinder {
    
        let event: UIControl.Event
        let button: UIButton
        let handler: UIButton.EventHandler
        let selector: Selector
    
        required init(
            _ event: UIControl.Event,
            on button: UIButton,
            withHandler handler: @escaping UIButton.EventHandler
        ) {
            self.event = event
            self.button = button
            self.handler = handler
            self.selector = #selector(performEvent(on:ofType:))
            button.addTarget(self, action: self.selector, for: event)
        }
    
        deinit {
            button.removeTarget(self, action: selector, for: event)
            if let index = bindedEvents.index(forKey: button) {
                bindedEvents.remove(at: index)
            }
        }
    }
    
    private extension EventBinder {
    
        @objc func performEvent(on sender: UIButton, ofType event: UIControl.Event) {
            handler(sender, event)
        }
    }
    
    extension UIButton {
    
        typealias EventHandler = (UIButton, UIControl.Event) -> Void
    
        func on(_ event: UIControl.Event, handler: @escaping EventHandler) {
            bindedEvents[self] = EventBinder(event, on: self, withHandler: handler)
        }
    }
    

    我使用自定义类来绑定事件的原因是为了能够稍后在取消初始化按钮时释放引用。这将防止发生可能的内存泄漏。这在 UIButton 的扩展中是不可能的,因为我不允许实现属性或 deinit 方法。

    【讨论】:

      【解决方案6】:

      如果你有像我这样的循环按钮,你可以试试这样的

      var buttonTags:[Int:String]? // can be [Int:Any]
      let myArray = [0:"a",1:"b"]
      for (index,value) in myArray {
      
           let button = // Create a button
      
           buttonTags?[index] = myArray[index]
           button.tag = index
           button.addTarget(self, action: #selector(buttonAction(_:)), for: .touchDown)
      
      }
      @objc func buttonAction(_ sender:UIButton) {
      
          let myString = buttonTags[sender.tag]
      
      }
      

      【讨论】:

        【解决方案7】:

        Swift 4.0 代码(又来了)

        被调用的动作应该这样标记,因为这是将函数导出为目标c语言的swift函数的语法。

        @objc func deleteAction(sender: UIButton) {
        }
        

        创建一些工作按钮:

        let deleteButton = UIButton(type: .roundedRect)
        deleteButton.setTitle("Delete", for: [])
        deleteButton.addTarget(self, action: #selector( 
        MyController.deleteAction(sender:)), for: .touchUpInside)
        

        【讨论】:

        • 感谢分享。这行得通,但用@objc 标记它感觉不直观。你能解释一下原因吗?
        • @rawbee 同意这是违反直觉的。这个问题有更多背景:stackoverflow.com/questions/44390378/…
        【解决方案8】:

        Swift 5.0 代码

        我使用 theButton.tag 但如果我有很多类型的选项,它的开关盒会很长。

        theButton.addTarget(self, action: #selector(theFunc), for: .touchUpInside) 
        theButton.frame.name = "myParameter"
        

        .

        @objc func theFunc(sender:UIButton){ 
        print(sender.frame.name)
        }
        

        【讨论】:

          【解决方案9】:

          这是一个更重要的评论。共享开箱即用的 sytanx 参考。对于黑客解决方案,请查看其他答案。

          Per Apple's docs, Action Method Definitions 必须是这三个之一。其他任何事情都是不可接受的。

          @IBAction func doSomething()
          @IBAction func doSomething(sender: UIButton)
          @IBAction func doSomething(sender: UIButton, forEvent event: UIEvent)
          

          【讨论】:

            【解决方案10】:

            适用于 Swift 2.X 及更高版本

            button.addTarget(self,action:#selector(YourControllerName.buttonClicked(_:)),
                                     forControlEvents:.TouchUpInside)
            

            【讨论】:

              【解决方案11】:

              Swift 3.0 代码

              self.timer = Timer.scheduledTimer(timeInterval: timeInterval, target: self, selector:#selector(fetchAutocompletePlaces(timer:)), userInfo:[textView.text], repeats: true)
              
              func fetchAutocompletePlaces(timer : Timer) {
              
                let keyword = timer.userInfo
              }
              

              您可以在 'userinfo' 中发送值并将其用作函数中的参数。

              【讨论】:

                猜你喜欢
                • 2018-12-04
                • 2012-01-18
                • 1970-01-01
                • 2019-05-03
                • 1970-01-01
                • 2018-02-14
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多