在Swift中以编程方式设置操作监听器

73

我看到一些示例代码将相同的 OnClick 事件分配给 Android 中的所有按钮(即使它们执行完全不同的操作)。如何在 Swift 中实现这个功能?

Android 示例:

@Override 
public void onCreate(Bundle savedInstanceState) {
        button1.setOnClickListener(onClickListener);
        button2.setOnClickListener(onClickListener);
        button3.setOnClickListener(onClickListener);
} 

private OnClickListener onClickListener = new OnClickListener() {
     @Override 
     public void onClick(View v) {
         switch(v.getId()){
             case R.id.button1:
                  //DO something 
             break; 
             case R.id.button2:
                  //DO something 
             break; 
             case R.id.button3:
                  //DO something 
             break; 
         } 

   } 
}; 

注意:我不想以编程方式创建按钮。

1
可能是如何在Swift中以编程方式创建按钮?的重复问题。 - chris
5
不...我需要将相同的监听器添加到多个按钮上...这不是重复的。 - Gilberto Ibarra
1
在每个按钮上调用addTarget方法,指向相同的操作? - chris
1
UIControl有一个方法-addTarget,可以指向同一个“监听器”UIControl。但是从你的代码来看,为什么不使用4个监听器,而不是一个,以确定哪个按钮被按下? - cream-corn
@cream-corn 因为您可能具有动态表结构,其中包含可以使用行索引进行映射的按钮。 - Marcin D
3个回答

96
在iOS平台上,你不是设置监听器(listener),而是向你的UIControl对象(UIButton是其子类)添加一个目标(target)和一个操作(action,即方法签名,在iOS术语中称为“选择器(selector)”)。
button1.addTarget(self, action: "buttonClicked:", for: .touchUpInside)
button2.addTarget(self, action: "buttonClicked:", for: .touchUpInside)
button3.addTarget(self, action: "buttonClicked:", for: .touchUpInside)
第一个参数是目标对象,在本例中为self。动作是选择器(方法签名),基本上有两种选项(稍后详细介绍)。控件事件有点特殊,适用于UIControl - .TouchUpInside通常用于点击按钮。
现在是动作部分。这是一个方法(名称由您决定)具有以下格式之一:
func buttonClicked()
func buttonClicked(_ sender: AnyObject?)

要使用第一个,请使用“buttonClicked”,对于第二个(您想要的),请使用带有尾随冒号的“buttonClicked:”。sender将是事件源,换句话说,是您的按钮。

func buttonClicked(_ sender: AnyObject?) {
  if sender === button1 {
    // do something
  } else if sender === button2 {
    // do something
  } else if sender === button3 {
    // do something
  }
}

(这假定 button1 , button2 和 button3 是实例变量)。

不要使用一个大的switch语句的方法,考虑使用每个按钮的单独方法。根据您特定的用例,任何一种方法可能更好:

func button1Clicked() {
  // do something
}

func button2Clicked() {
  // do something
}

func button3Clicked() {
  // do something
}

这里,我甚至没有使用sender参数,因为我不需要它。

P.S.:您可以在Storyboard或nib文件中添加目标和操作,而无需以编程方式添加。为了公开操作,您需要在函数前面加上IBAction,例如:

@IBAction func button1Clicked() {
  // do something
}

76

Swift 4.*

button.addTarget(self, action: #selector(didButtonClick), for: .touchUpInside)

这个按钮会触发这个函数:

@objc func didButtonClick(_ sender: UIButton) {
    // your code goes here
}

1
现在应该写成 #selector(buttonClicked(sender:)) 或使用 #selector(buttonClicked(_:))btnAll.addTarget(self, action: #selector(buttonClicked(_:)), for: .touchUpInside) 然后将您的函数更改为:func buttonClicked(_ sender : UIButton){.... } - Vitaliy A
4
如果我为所有按钮设置了同一个函数,那么如何检查哪个按钮被点击了? - user25
为了检查哪个按钮被点击,您在创建它们时设置不同的标签。button.tag = 1_(或2或3)。然后您使用:_switch sender.tag {case 1:... - user2888102

3

一种Swift 5扩展解决方案

创建一个名为“SetOnClickListener.swift”的Swift文件,
复制并粘贴以下代码

import UIKit

class ClosureSleeve {
  let closure: () -> ()

  init(attachTo: AnyObject, closure: @escaping () -> ()) {
    self.closure = closure
    objc_setAssociatedObject(attachTo, "[\(arc4random())]", self, .OBJC_ASSOCIATION_RETAIN)
  }

  @objc func invoke() {
    closure()
  }
}

extension UIControl {
    func setOnClickListener(for controlEvents: UIControl.Event = .primaryActionTriggered, action: @escaping () -> ()) {
        let sleeve = ClosureSleeve(attachTo: self, closure: action)
        addTarget(sleeve, action: #selector(ClosureSleeve.invoke), for: controlEvents)
    }
}

使用方法
例如,buttonA 是一个 UIButton 按钮。

buttonA.setOnClickListener {
  print("button A clicked")                  
}

如何获取目标元素,例如在 @objc func didButtonClick(_ sender: UIButton) 中的 "sender"? // 你的代码写在这里 - Nasib

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