iOS Swift – 如何以编程方式为所有按钮指定默认操作

我正在开发原型阶段的应用程序。 某些界面元素没有通过故事板或编程方式分配给它们的任何操作。

根据UX准则,我想在应用程序中找到这些“非活动”按钮,并在测试期间点击时显示“function不可用”警报。 这可以通过扩展UIButton来完成吗?

除非通过界面生成器或以编程方式分配其他操作,否则如何为UIButton分配默认操作以显示警报?

那么你想要实现的目标是什么。 我已经使用UIViewController扩展并添加了一个闭包作为没有目标的按钮的目标。 如果按钮没有动作,则会显示警报。

 class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() self.checkButtonAction() } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) } @IBAction func btn_Action(_ sender: UIButton) { } } extension UIViewController{ func checkButtonAction(){ for view in self.view.subviews as [UIView] { if let btn = view as? UIButton { if (btn.allTargets.isEmpty){ btn.add(for: .touchUpInside, { let alert = UIAlertController(title: "Test 3", message:"No selector", preferredStyle: UIAlertControllerStyle.alert) // add an action (button) alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: nil)) // show the alert self.present(alert, animated: true, completion: nil) }) } } } } } class ClosureSleeve { let closure: ()->() init (_ closure: @escaping ()->()) { self.closure = closure } @objc func invoke () { closure() } } extension UIControl { func add (for controlEvents: UIControlEvents, _ closure: @escaping ()->()) { let sleeve = ClosureSleeve(closure) addTarget(sleeve, action: #selector(ClosureSleeve.invoke), for: controlEvents) objc_setAssociatedObject(self, String(format: "[%d]", arc4random()), sleeve, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN) } } 

我测试过了。 希望这可以帮助。 快乐的编码。

由于您无法覆盖扩展中的方法,因此剩下的唯一选项是:
1.子类你的按钮 – 但可能不是你想要的,因为我假设你想对现有的按钮使用这个function
2.方法调配 – 改变现有函数的实现,即init

除非通过界面生成器或以编程方式分配其他操作,否则如何为UIButton分配默认操作以显示警报?

我建议做方法调配

通过调配,可以通过更改特定选择器(方法)与包含其实现的函数之间的映射,在运行时将方法的实现替换为不同的方法。

https://www.uraimo.com/2015/10/23/effective-method-swizzling-with-swift/

备注:我建议查看上面的文章。

作为您问题的准确答案,以下代码段应该是 – 通常 – 您要实现的目标是什么:

 class ViewController: UIViewController { // MARK:- IBOutlets @IBOutlet weak var lblMessage: UILabel! // MARK:- IBActions @IBAction func applySwizzlingTapped(_ sender: Any) { swizzleButtonAction() } @IBAction func buttonTapped(_ sender: Any) { print("Original!") lblMessage.text = "Original!" } } extension ViewController { func swizzleButtonAction() { let originalSelector = #selector(buttonTapped(_:)) let swizzledSelector = #selector(swizzledAction(_:)) let originalMethod = class_getInstanceMethod(ViewController.self, originalSelector) let swizzledMethod = class_getInstanceMethod(ViewController.self, swizzledSelector) method_exchangeImplementations(originalMethod, swizzledMethod) } func swizzledAction(_ sender: Any) { print("Swizzled!") lblMessage.text = "Swizzled!" } } 

在调用swizzleButtonAction() – 通过点击“Apply Swizzling”按钮( applySwizzlingTapped ) – ,“Button”的选择器应该从buttonTapped变为swizzledAction

输出:

在此处输入图像描述

我将通过使用IBOutlet集合变量实现此类函数,然后在实现该function后立即从该集合中删除按钮。 像这样的东西:

 class ViewController { @IBOutlet var inactiveBtns: [UIButton]! func viewDidLoad() { inactiveBtns.forEach { (button) in button.addTarget(self, action: #selector(showDialog), for: UIControlEvents()) } } func showDialog() { // show the dialog } } 

IBOutlet在界面构建器中可见,因此您可以在其中添加多个按钮。