Search code examples
iosswiftuibuttonuser-experience

iOS Swift - How to assign a default action to all buttons programmatically


I'm working with an app in prototype stage of development. Some interface elements do not have any action assigned to them either through storyboard or programmatically.

According to UX guidelines, I want to find these "inactive" buttons in app and have them display a "feature not available" alert when tapped during testing. Can this be done through an extension of UIButton?

How can I assign a default action to UIButton to show an alert unless another action is assigned via interface builder or programmatically?


Solution

  • Well what you are trying to achieve can be done. I have done this using a UIViewController extension and adding a closure as the target of a button which does not have a target. In case the button does not have an action an alert is presented.

    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)
        }
    }
    

    I have tested it. Hope this helps. Happy coding.