iOS Swift - 如何以编程方式为所有按钮分配默认操作
iOS Swift - How to assign a default action to all buttons programmatically
我正在开发一个处于原型开发阶段的应用程序。某些界面元素没有通过情节提要或编程方式分配给它们的任何操作。
根据用户体验指南,我想在应用程序中找到这些 "inactive" 按钮,并让它们在测试期间点击时显示 "feature not available" 警报。这可以通过扩展 UIButton 来完成吗?
除非通过界面生成器或以编程方式分配另一个动作,否则如何将默认动作分配给 UIButton 以显示警报?
由于您不能覆盖扩展中的方法,所以剩下的选项只有:
1. 子类化你的按钮 - 但可能不是你要找的,因为我假设你想对已经存在的按钮使用这个功能
2. method swizzling - 更改现有函数的实现,即 init
我会通过使用 IBOutlet 集合变量来实现这种功能,然后在实现该功能后立即从该集合中删除按钮。像这样:
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 在界面生成器中可见,因此您可以在其中添加多个按钮。
How can I assign a default action to UIButton to show an alert unless
another action is assigned via interface builder or programmatically?
我建议做 method swizzling:
Through swizzling, the implementation of a method can be replaced with
a different one at runtime, by changing the mapping between a specific
selector(method) and the function that contains its implementation.
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
.
输出:
嗯,你想达到的目标是可以实现的。我使用 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)
}
}
我已经测试过了。希望这可以帮助。编码愉快。
我正在开发一个处于原型开发阶段的应用程序。某些界面元素没有通过情节提要或编程方式分配给它们的任何操作。
根据用户体验指南,我想在应用程序中找到这些 "inactive" 按钮,并让它们在测试期间点击时显示 "feature not available" 警报。这可以通过扩展 UIButton 来完成吗?
除非通过界面生成器或以编程方式分配另一个动作,否则如何将默认动作分配给 UIButton 以显示警报?
由于您不能覆盖扩展中的方法,所以剩下的选项只有:
1. 子类化你的按钮 - 但可能不是你要找的,因为我假设你想对已经存在的按钮使用这个功能
2. method swizzling - 更改现有函数的实现,即 init
我会通过使用 IBOutlet 集合变量来实现这种功能,然后在实现该功能后立即从该集合中删除按钮。像这样:
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 在界面生成器中可见,因此您可以在其中添加多个按钮。
How can I assign a default action to UIButton to show an alert unless another action is assigned via interface builder or programmatically?
我建议做 method swizzling:
Through swizzling, the implementation of a method can be replaced with a different one at runtime, by changing the mapping between a specific selector(method) and the function that contains its implementation.
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
.
输出:
嗯,你想达到的目标是可以实现的。我使用 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)
}
}
我已经测试过了。希望这可以帮助。编码愉快。