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
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
Filtering Dictionary in Swift 4 Fails in Xcode, But Succeeds in Playground
How to Read Ansi Escape Code Response Value in Swift
Xctest Unit Test Data Response Not Set in Test After Viewdidload
How to Execute Multiplications And/Or Divisions in the Right Order
Disable Email Detection in Swiftui's Text
Stopping Timer at Defined Amount of Time in Swift
Printed Double Value Differs from Originally Set Value
How to Make Struct Lazylist in Swiftui
Exc_Bad_Instruction Only in iPhone 5 Simulator
How to Handle Unsafepointer<Unmanaged<Cfarray>>
Swift Can Not Save .M3U8 File to Gallery
Ternary Operator Inside Navigationlink Swiftui
Swift Enum with Custom Initializer Loses Rawvalue Initializer
How to Procedurally Draw Rectangle/Lines in Swift Using Cgcontext