I'd like to have a protocol:
protocol CameraButtonDelegate: class {
func cameraButtonDidPress(_ sender: UIButton)
}
So, I could assign any client to a button, e.g.:
cameraButton.addTarget(delegate, action: #selector(cameraButtonDidPress), for: .touchUpInside)
However, it does not compile as I have to specify a particular function in action, e.g.:
cameraButton.addTarget(delegate, action: #selector(AAPLViewController.cameraButtonDidPress), for: .touchUpInside)
How to solve this issue, if I'd like to have multiple clients being targeted by a single button?
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)
You can try
cameraButton.addTarget(Service.shared, action: #selector(Service.shared.cameraButtonDidPress(_:)), for: .touchUpInside)
//
class Service {
static let shared = Service()
#objc func cameraButtonDidPress (_ sender:UIButton){
}
}
Related
I am trying to execute two functions in another view by pressing two buttons. Each button should call one of the functions, that is passed on through delegation.
But it seems like when I am pressing one of the buttons they execute both functions of the protocol simultaneously.
Only when I comment/remove one function it works.
The protocol:
protocol ManipulateSectionDelegate {
func addSection()
func removeSection()
}
The MainView:
Here are the functions that should get called, when pressing the buttons in the second View
extension MainViewController: ManipulateSectionDelegate{
#objc func showSettingsView(){
let settingsController = SettingsViewController()
settingsController.delegateManipuplateSection = self
navigationController?.pushViewController(settingsController, animated: true)
}
func addSection(){
semesterList.append(Semester.init(subject: [], done: false))
subjectTableView.reloadData()
print(semesterList)
}
func removeSection(){
semesterList.removeLast()
subjectTableView.reloadData()
print(semesterList)
}
}
The second View:
class SettingsViewController:UIViewController{
var delegateManipuplateSection: ManipulateSectionDelegate?
override func viewDidLoad() {
super.viewDidLoad()
addSectionButton.addTarget(self, action: #selector(addSectionButtonPressed), for: .touchUpInside)
addSectionButton.addTarget(self, action: #selector(removeSectionButtonPressed), for: .touchUpInside)
}
}
extension SettingsViewController{
#objc func addSectionButtonPressed(){
delegateManipuplateSection?.addSection()
}
#objc func removeSectionButtonPressed(){
delegateManipuplateSection?.removeSection()
}
}
Even though each button is connected to a different function and should execute only one of them, both get executed.
Is this a situation where I would have to use observers and notifications? Did I do something wrong?
You connected two actions to one same button
addSectionButton.addTarget(self, action: #selector(addSectionButtonPressed), for: .touchUpInside)
addSectionButton.addTarget(self, action: #selector(removeSectionButtonPressed), for: .touchUpInside)
I want to create a function that adds a refreshControll to a UITableView.
The problem I have is the #selector:
extension UITableView{
func refreshTableView(callFunction: #selector()){
refreshControl = UIRefreshControl()
refreshControl?.attributedTitle = NSAttributedString(string: "Pull to refresh")
refreshControl?.addTarget(self, action: #selector(callFunction), for: .valueChanged)
self.addSubview(refreshControl!)
}
}
I want to specify which function to call when I use this function.
Is this even possible?
#selector(...) is an expression of type Selector, so your method should accept a Selector object and pass it directly to the action: parameter.
extension UITableView {
func refreshTableView(callFunction: Selector) {
// ...
refreshControl?.addTarget(self, action: callFunction, for: .valueChanged)
// ...
}
}
myTableView.refreshTableView(callFunction: #selector(whatever))
The following code is located inside a subclass of UIView
I am setting up a cancelButton inside a closure:
private var cancelButtonClosure: UIButton = {
...
button.addTarget(self, action: #selector(cancel(_:)), for: .touchUpInside)
...
}()
And at first I instantiated the button inside a function like so:
func showConfirmationView(...) {
...
let cancelButton = self.cancelButtonClosure
...
addSubview(cancelButton)
...
}
However this resulted in the cancel function not being called at all (even though the layout was right and the button was highlighting)
So I made these change:
Removed the addTarget part from the cancelButtonClosure
Added the addTarget part inside the showConfirmationView function
So it looked like that:
func showConfirmationView(...) {
...
let cancelButton = self.cancelButtonClosure
cancelButton.addTarget(self, action: #selector(cancel(_:)), for: .touchUpInside)
...
addSubview(cancelButton)
...
}
It worked: the cancel function was called; but I don't know why. I'm really curious to know why what I did before did not work. Thanks for your insights!
Check your implementation because a setup like this works as expected:
private var cancelButton: UIButton = {
let btn = UIButton(type: .system)
btn.setTitle("Cancel", for: .normal)
btn.addTarget(self, action: #selector(cancelSomething(_:)), for: .touchUpInside)
return btn
}()
#objc func cancelSomething(_ sender: UIButton) {
print("Something has to be cancelled")
}
override func viewDidLoad() {
super.viewDidLoad()
showConfirmationView()
}
func showConfirmationView() {
cancelButton.sizeToFit()
cancelButton.center = view.center
view.addSubview(cancelButton)
}
I have a class where written is a function creating my button:
LoginButton.swift
func createButton() {
let myButton: UIButton = {
let button = UIButton()
button.addTarget(self, action: #selector(Foo().buttonPressed(_:)), for: .touchUpInside)
}()
}
Now in my second class, Foo.swift, I have a function that just prints a statement
Foo.swift
#objc func buttonPressed(_ sender: UIButton) {
print("button was pressed")
}
When ran I get no errors except when I try to press the button, nothing happens. Nothing prints, the UIButton doesn't react in any way. Really not sure where the error occurs because Xcode isn't printing out any type of error or warning message.
The action method is called in the target object. Thus, you have either to move buttonPressed to the class which contains createButton or to pass an instance of Foo as a target object.
But note that a button is not the owner of its targets. So, if you just write:
button.addTarget(Foo(), action: #selector(buttonPressed(_:)), for: .touchUpInside)
This will not work, because the Foo object is immediately released after that line. You must have a strong reference (e.g. a property) to Foo() like
let foo = Foo()
func createButton() {
let myButton: UIButton = {
let button = UIButton()
button.addTarget(foo, action: #selector(buttonPressed(_:)), for: .touchUpInside)
}()
}
You are missing with target. So make instant of target globally and make use of it as target for button action handler.
class ViewController: UIViewController {
let foo = Foo()
override func viewDidLoad() {
super.viewDidLoad()
createButton()
}
func createButton() {
let myButton: UIButton = {
let button = UIButton(frame: CGRect(x: 0, y: 0, width: 100, height: 50))
button.backgroundColor = UIColor.red
button.setTitle("Tap me", for: .normal)
button.addTarget(self.foo, action: #selector(self.foo.buttonPressed(_:)), for: .touchUpInside)
return button
}()
myButton.center = self.view.center
self.view.addSubview(myButton)
}
}
Class Foo:
class Foo {
#objc func buttonPressed(_ sender: UIButton) {
print("button was pressed")
}
}
Just pass Selector as function argument.
func createButtonWith(selector: Selector) {
let myButton: UIButton = {
let button = UIButton()
button.addTarget(self, action: selector), for: .touchUpInside)
}()
}
And call this function like below...
createButtonWith(selector: #selector(Foo().buttonPressed(_:)))
I have subclassed UIButton and want to call a delegate method just once when the button goes into the highlighted state and call it again just once when it goes into the unhighlighted state:
override var isHighlighted: Bool {
didSet {
if isHighlighted {
delegate?.buttonHighlightStateDidChange(highlighted: true)
} else {
delegate?.buttonHighlightStateDidChange(highlighted: false)
}
}
}
However when I touch down on the button it seems that didSet is getting repeatedly called. What am I doing wrong here? How can I call the delegate method just once?
I would recommend against using your subclass in this way. UIControl has a builtin mechanism for getting callbacks in response to control events:
func registerActions(for button: UIButton) {
button.addTarget(self, action: #selector(MyClass.buttonIsHighlighted(sender:)), for: .touchDown)
button.addTarget(self, action: #selector(MyClass.buttonIsUnHighlighted(sender:)), for: .touchUpInside)
button.addTarget(self, action: #selector(MyClass.buttonIsUnHighlighted(sender:)), for: .touchUpOutside)
}
func buttonIsHighlighted(sender: UIButton) {
// highlighted
}
func buttonIsUnHighlighted(sender: UIButton) {
// unhighlighted
}