iOS Swift-как программно назначить действие по умолчанию всем кнопкам
Я работаю с приложением на стадии прототипа разработки. Некоторые элементы интерфейса не имеют никаких действий, назначенных им либо через раскадровку, либо программно.
согласно рекомендациям UX, я хочу найти эти "неактивные" кнопки в приложении и отобразить предупреждение "функция недоступна" при нажатии во время тестирования. Можно ли это сделать через расширение UIButton?
как я могу назначить действие по умолчанию для UIButton, чтобы показать предупреждение если другое действие не назначено через interface builder или программно?
4 ответов
хорошо, что вы пытаетесь достичь можно сделать. Я сделал это, используя 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. подкласс ваших кнопок - но, вероятно, не то, что вы ищете, потому что я предполагаю, что вы хотите использовать эту функциональность для уже существующих кнопок
2. метод swizzling-изменить реализацию существующей функции, т. е. init
как я могу назначить действие по умолчанию для UIButton, чтобы показать предупреждение, если другое действие назначается через interface builder или программно?
Я бы предложил делать метод swizzling:
через swizzling, вставку метода можно заменить с другой во время выполнения, путем изменения отображения конкретного селектор(метод) и функция, содержащая его реализация.
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()
- нажав кнопку" Применить Swizzling" (applySwizzlingTapped
)-, селектор "Кнопка" должна изменяться с buttonTapped
to swizzledAction
.
выход:
Я бы реализовал эту функцию, используя переменную коллекции IBOutlet, а затем удалив кнопки из этой коллекции, как только функция будет реализована. что-то вроде этого:--2-->
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 отображается в построителе интерфейса, поэтому вы можете добавить туда несколько кнопок.