Swift - Protocol as Type as Target of Button Action

Swift - protocol as type as target of button action

I like Andrius approach, but to answer the question, you have to add the class keyword to your protocol’s inheritance list:

protocol CloseViewProtocol: class {
func closeViewAction(sender: UIButton!)
}

Swift: Protocol method as Action in target-action

It should work if you make the protocol @objc

@objc protocol CameraButtonDelegate: class {
func cameraButtonDidPress(_ sender: UIButton
}

and specify the selector as protocol method

cameraButtonDidPress.addTarget(delegate, action: #selector(CameraButtonDelegate.cameraButtonDidPress), for: .touchUpInside)

Adding a closure as target to a UIButton

Do Not Use This Answer, See Note Below

NOTE:
like @EthanHuang said
"This solution doesn't work if you have more than two instances. All actions will be overwrite by the last assignment."

Keep in mind this when you develop, i will post another solution soon.

If you want to add a closure as target to a UIButton, you must add a function to UIButton class by using extension

Swift 5

import UIKit    
extension UIButton {
private func actionHandler(action:(() -> Void)? = nil) {
struct __ { static var action :(() -> Void)? }
if action != nil { __.action = action }
else { __.action?() }
}
@objc private func triggerActionHandler() {
self.actionHandler()
}
func actionHandler(controlEvents control :UIControl.Event, ForAction action:@escaping () -> Void) {
self.actionHandler(action: action)
self.addTarget(self, action: #selector(triggerActionHandler), for: control)
}
}

Older

import UIKit

extension UIButton {
private func actionHandleBlock(action:(() -> Void)? = nil) {
struct __ {
static var action :(() -> Void)?
}
if action != nil {
__.action = action
} else {
__.action?()
}
}

@objc private func triggerActionHandleBlock() {
self.actionHandleBlock()
}

func actionHandle(controlEvents control :UIControlEvents, ForAction action:() -> Void) {
self.actionHandleBlock(action)
self.addTarget(self, action: "triggerActionHandleBlock", forControlEvents: control)
}
}

and the call:

 let button = UIButton()
button.actionHandle(controlEvents: .touchUpInside,
ForAction:{() -> Void in
print("Touch")
})

Calling a Protocol Function from A Selector in Swift

@objc
protocol MyProtocol: class {
func functionOne()
}

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

The above syntax doesn't care where the protocol is implemented. It only cares about the name of the function and its signature. Because of this behaviour, we say MyProtocol.functionOne and that tells it enough. Some class conforms to that and implements a function with that signature and name.

Swift: Creating a button that calls a method in another class1

Assuming that you have a view controller where you create the Store object - you should pass the button action back to this view controller and add a segue from it to your desired destination.

Best practice is to use a protocol that delegates the buttons action back up to the viewController that it is contained in as below.

Store.swift

protocol StoreDelegate: NSObject {
func didPressButton(button:UIButton)
}

class Store: UIView {

weak var delegate:StoreDelegate!

override init(frame:CGRect) {
super.init(frame:frame)

var button = UIButton()
button.setTitle("button", forState: .Normal)
button.addTarget(self, action: "buttonPress:", forControlEvents: .TouchUpInside)
self.addSubview(button)
}

func buttonPress(button:UIButton) {
delegate.didPressButton(button)
}

}

ViewController.swift

class ViewController: UIViewController, StoreDelegate {

override func viewDidLoad() {
super.viewDidLoad()
addStoreObj()
}

func addStoreObj() {
var store = Store()
store.delegate = self // IMPORTANT
self.view.addSubview(store)
}

func didPressButton(button:UIButton) {
self.performSegueWithIdentifier("ok", sender: nil)
}

}

This code is untested, but I hope you get the idea - your Store object delegates the button press activity back to its containing ViewController and then the ViewController carries out the segue that you have attached to it in the Storyboard.

Action on a UIButton doesn't work

Move these lines to before you set your button's target:

let viewModel = GreetingViewModel(person: model)
self.viewModel = viewModel

Right now you are adding self.viewModel as the target, but self.viewModel is nil

Add action to button created using UIViewRepresentable

Here is a demo of possible solution - we need a wrapper between UIKit objective-c selectors and SwiftUI swift function.

Tested with Xcode 13.3 / iOS 15.4

demo

Here is main part (used UIButton instead of FluentUI.Button for simplicity):

Anything(UIButton(type: .system)) {
$0.setTitle("Try me!", for: .normal)

$0.addTarget(toggleColor, action: #selector(Action.perform(sender:)), for: .touchUpInside)
toggleColor.action = {
isGreen.toggle()
}
}

Complete test module is here

UIButton Target Action inside UIView

I thinks it is not a good idea to know inside the view about parent structures. It is breaks encapsulation and leads to hard maintenance, bugs and extra relations.

As a simple way you can define function addTarget for your custom UIView, that will work like a bridge.

class UICustomView {
func addTarget(target: AnyObject, action: Selector, forControlEvents: UIControlEvents) {
menuControlButton.addTarget(target, action: action, forControlEvents: forControlEvents)
}

func setupViews() {...}

func toggleButton(sender: MenuControlButton!) {...}

class SomeOtherViewController {

func someInitFunc {
let viewWithButton = UICustomView()
viewWithButton.addTarget(self, "foo:", .TouchUpInside)
}

func foo(sender: AnyObject) {
let isActive = sender.toggleActive() // switches button image and returns whether active or not after
NSUserDefaults.standardUserDefaults().setBool(isActive, forKey: sender.title)
self.reloadCalendar()
}
}

Then your controller only knows that UICustomView can handle events and UICustomView have know nothing about SomeOtherViewController

If you have more than one button inside you custom view perhaps it would be a good idea to implement something like UIAlertController with type UIAlertControllerStyle.ActionSheet



Related Topics



Leave a reply



Submit