I want to define action for my KCFloatingActionButton. UITapGestureRecognizer is defined for the fab button in ViewController, but I want to do this action in manager class or on another page. I got the KCFloatingActionButton view, but I can't give it a click action.When I click on KCFloatingActionButton I don't want to define a new UITapGestureRecognizer with addGestureRecognizer ,I want it to take the action it is defined.
I can do this for UIButton or UIBarButtonItem as follows
button.sendActions(for: .touchUpInside)
barButtonItem.target?.perform(barButtonItem.action, with: nil)
How can I do this action for KCFloatingActionButton ?
You can try something like this:
button.addTarget(self, action: #selector(handleButtonAction), for: .touchUpInside)
#objc func handleButtonAction(){
//Put your logic here
}
The self in addTarget refers where will be the action to be executed, in this case will be in the current ViewController.
The #selector(handleButtonAction) is the function that will be executed when the user touch the button, in this case will be an Objective-C function (that's why is use the "#selector").
The .touchUpInside is the event that has to be triggered to execute the function.
And finally the function itself #objc func handleButtonAction() { } will execute all the actions that you define for the touch up event.
the #objc attribute comes in: when you apply it to a class or method it instructs Swift to make those things available to Objective-C as well as Swift code.
Related
I added a button in the main storyboard in Xcode. This button has an image as the background and its title "blueDoor" is shown on top of the background (see photo below).
There will be three buttons like this and they are linked to one IBAction. I would like to use sender.currentTitle to let the program know which button is clicked, but I don't want the text to show on the image.
How can I hide the text but still keep the title attribute so sender.currentTitle can be used? Or is there another way to do so?
A button with an image as the background:
You can use tag to do this.
Open storyboard > select your button > select Show the Attributes Inspector section in the right bar of the Xcode > scroll down and find Tag under the View section. And give different tags to your buttons.
Then your IBAction should be like this ->
#IBAction func didTapMyButtons(_ sender: UIButton) {
switch sender.tag:
case 0: // it is your first button's tag
// Do something with button which has tag `0`
case 1: // it is your second button's tag
// Do something with button which has tag `1`
case 2: // it is your third button's tag
// Do something with button which has tag `2`
default: break
}
Shortly: you can not do that. The currentTitle property is essentially a getter for button.titleLable.text. So is title(for: UIControl.State) setter (setTitle(String?, for: UIControl.State))
What docs say:
var currentTitle: String? { get }
Discussion
The value for this property is set automatically whenever
the button state changes. For states that do not have a custom title
string associated with them, this method returns the title that is
currently displayed, which is typically the one associated with the
normal state. The value may be nil.
This means whatever changes you bring to titleLabel.text, it is automatically reflected on a currentTitle property.
You can use tags as suggested or add a custom action handler for every button as you create them. E.g. for cancel and proceed buttons one will do it like so:
self.cancelButton.addTarget(self, action: #selector(self.cancelActionMethod), for: .touchUpInside)
self.proceedButton.addTarget(self, action: #selector(self.proceedActionMethod), for: .touchUpInside)
And then somewhere inside self:
#objc func cancelActionMethod() {
// some cancel specific actions
}
#objc func proceedActionMethod() {
// some cancel specific actions
}
I have a function that creates a button with a selector function as a target. The address of a button gets passed to handleSelectPhoto.
lazy var image1Button = createButton(selector: #selector(handleSelectPhoto))
func createButton(selector: Selector) -> UIButton {
let button = UIButton(type: .system)
button.addTarget(self, action: selector, for: .touchUpInside)
return button
}
#objc func handleSelectPhoto(button: UIButton) {
// Do something with button, this works
}
Now, I am trying to change the class of the above from UIButton to UIImageView like the following,
lazy var image1Button = createButton(selector: #selector(handleSelectPhoto))
func createButton(selector: Selector) -> UIImageView {
let view = UIImageView()
view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: selector))
view.isUserInteractionEnabled = true
return view
}
#objc func handleSelectPhoto(button: UIImageView) {
// HERE, button does not get passed
}
With the above changes, in handleSelectPhoto, button instance is not correct. I can not read it as UIImageView type.
If I add a selector function using addGestureRecognizer, does it behave differently than adding a selector function using addTarget, in terms of how selector function is executed with parameters? Maybe I am not understanding how this selector function works...
Adding a target to something like UIGestureRecognizer or UIButton only passes one parameter to the selected function. This parameter depends on the type you are about to add the target on.
In your case the first code snippet works because you are adding a target to an UIButton, so your selected function gets passed this UIButton instance.
In your second scenario you add the target to an UITapGestureRecognizer, so the passed instance will be exactly this gesture recognizer, which cannot be of type UIImageView.
So the difference from the target parameter perspective between UIGestureRecognizer and UIButton is no difference. They both pass their instances to the selected function.
From the UIView subclass perspective there is the difference that UIGestureRecognizer is not a subclass of UIView, but UIButton is. That's why you can just use the passed UIButton instance in your first snippet. In the second snippet you need use the view property of UIGestureRecognizer.
guard let imageView = gestureRecognizer.view as? UIImageView else { return }
Besides your actual question it seems important to clarify how to write #selectors correctly. You're doing it correct already. No change necessary. Some may say you need to add (_:) or : to your selector like so: #selector(handleSelectPhoto(_:)) but this isn't true. In general, you only need to add these special characters when you are selecting a method which has an overload method with a different amount of parameters, but the same base name.
You should make your tell while setting the selection that your function will accept a parameter by adding : at the end of method name.
lazy var image1Button = createButton(selector: #selector(handleSelectPhoto:))
UIKit will automatically understand that the selector methods parameter will be of type UITapGestureRecognizer. Now rewrite the below method like this and you will be good to go.
#objc func handleSelectPhoto(gesture: UITapGestureRecognizer) {
if let buttonImageView = gesture.view as? UIImageView {
//Here you can make changes in imageview what ever you want.
}
}
Super newb in Swift and iOS development here.
I am following this tutorial about implementing a custom control in a single view iOS app. It's a Swift 2 tutorial, but so far I'm doing OK transposing everything to 3 as I go (I use XCode 8 Beta).
I have a custom class, RatingControl, connected to a View in the storyboard.
In the class's constructor, I create a button:
let button = UIButton(frame: CGRect(x: 0, y: 0, width: 44, height: 44))
button.backgroundColor = UIColor.red()
Then, I try to assign an action to the button. The tutorial says I should do it like so:
button.addTarget(self, action: #selector(RatingControl.ratingButtonTapped(_:)),
for: .touchDown)
and then create, in the same RatingControl class, the method:
func ratingButtonTapped(button: UIButton) {
print("Button pressed 👍")
}
But when I compile, it complains:
type "RatingControl" has no member "ratingButtonTapped"
I've made 100% sure the function is there, in the class, and properly named. Full source
Is there something obvious I'm missing?
What I tried:
Added #objc to the class definition as per this answer (but that seems weird for a Swift-only thing, no?)
Made ratingButtonTapped() explicitly public (but that doesn't look like it should be necessary)
Fiddled around with strings instead of selectors, button.addTarget(self, action: "RatingControl.ratingButtonTapped", for: .touchDown) and many more, but that just crashes it later.
In Swift 3, method reference for: func ratingButtonTapped(button: UIButton) becomes ratingButtonTapped(button:).
So, using #selector(RatingControl.ratingButtonTapped(button:)) also work.
And if you want to keep #selector(RatingControl.ratingButtonTapped(_:)), then you need to declare the ratingButtonTapped method as:
func ratingButtonTapped(_ button: UIButton) { //<- `_`
print("Button pressed 👍")
}
And if you have only one ratingButtonTapped method in the class, you can address the selector as #selector(RatingControl.ratingButtonTapped) or simply (from inside RatingControl class) #selector(ratingButtonTapped).
This happened because Swift 3 has changed the way it handles the first parameter name. In Swift 3, all parameter names must be used when calling a function unless an explicit _ was declared as the parameter name.
What you used as your selector was fine if you had declared your function as:
func ratingButtonTapped(_ button: AnyObject) {
print("Button pressed 👍")
}
You could also have used this as your selector:
#selector(RatingControl.ratingButtonTapped(button:))
Added #objc to the class definition as per this answer (but that seems
weird for a Swift-only thing, no?)
Your code may be in Swift, but you are interacting with the Objective-C runtime when you are coding for Cocoa Touch (iOS framework). The selector is a function that needs to be visible to the Objective-C runtime. You get this for free most of the time because you are implementing this in a class that ultimately inherits from NSObject (like UIViewController). If you have a Swift only class that doesn't inherit from NSObject, then you can add #objc to make the class and methods visible to the Objective-C runtime.
If you want to call an action that is in your View Controller from a Different Class you can try this.
Use ViewController() for your target. Use ViewController.functionName for your selector. Do not use a helper method for the view controller variable like "vc", otherwise you will not be able to access objects within the ViewController.
Here is an example target:
self.addTarget(ViewController(), action:#selector(ViewController.Test(_:)), for: UIControlEvents.touchDragInside)
In your View Controller, here is an example Action
#IBAction func Test(_ sender: Any?) {
print("Goodtime was here")
}
In the target you must add () but not in the action's selector. You do not have to call #IBAction, it can just be func. Some people use #objc or public any of those prefixes on the action should work.
Review, if the action is in a different Class or ViewController, you must put the the Class reference in both the target and the action's selector. Otherwise, it will try to always call the action within the same file regardless if it is correct in the Selector. Likewise, if the action is in the same file use, self for the target and inside the action's selector.
Cheers
When adding button targets in swift 3 first you need to be in class.
class SomeClass {
// ...
let button = UIButton()
// configure button to taste...
button.addTarget(self,
action: #selector(doSomething),
for: .touchUpInside)
// ...
#objc public func doSomething() {
print("hello, world!")
}
// ...
}
I want an action to be done once when someone swipes within a button.
My current code is as follows:
let recogniser = UISwipeGestureRecognizer(target: self, action: "didTapButton2:")
recogniser.direction = .Up
button.addGestureRecognizer(recogniser)
func didTapButton2(sender: UIGestureRecognizer!) {
//Here I want to be able to recognise which button this was sent from (they are all tagged)
let button = sender. as UIButton //Gives an error
I need to use gesture recognisers as opposed to UIControlEvents as I need the event to only fire once. Using this makes the event fire loads of times - just need it to go once:
button.addTarget(self, action: "didTapButton2:", forControlEvents: .TouchDragInside)
Does anyone have a solution? Thanks
A UIGestureRecognizer has a property called view which is the view that it is attached to. View is declared as an optional, so you need to unwrap it:
if let button = sender.view as? UIButton {
// use button
if button.tag == 10 {
// handle button tagged with 10
}
}
I have a running project and now it is nearly completed but now i have a method in app delegate class and i want that this method should be called on all of the buttons in the project. One simple way is that i should add call to method in each button listener code.
But is there a way that i can make it wired with Button action "Touch up inside"
Override sendActionsForControlEvents by subclassing the UIButton class.
- (void)sendActionsForControlEvents:(UIControlEvents)controlEvents {
if(controlEvents == UIControlEventTouchUpInside) {
//call your method.
}
[super sendActionsForControlEvents:controlEvents];
}
You might have to go through all UIButtons and change its class to that of your subclass. I haven't tried this yet but I think you can give it a go.
You can use extension for this
extension UIButton
{
override open func sendActions(for controlEvents:UIControlEvents)
{
if controlEvents == .touchUpInside
{
// Type your custom execution here
}
}
}
This will get called for every event on button
swift 5
extension UIButton {
open override func sendAction(_ action: Selector, to target: Any?, for event: UIEvent?) {
//your code
super.sendAction(action, to: target, for: event)
}
}
I found this as a simplest way to implement a common action for every button click. This is way more simpler than subclassing every button in the app.