Adding gestures to a button in swift - ios

I'm trying to add gesture(swipe) functionality to an existing button in my view but I can't figure out how to attach the swipe to the button's area.
The desired effect is to have a button I can press as well as swipe to produce different outcomes. So far the way I'm implementing gestures applies it to my entire view not just the button.
I have a feeling it's pretty simple but it's been escaping me for a couple of days - I might just be searching for the wrong thing.
(I'm assigning '#IBOutlet var swipeButton: UIButton!' to my button BTW)
Code below:
class ViewController: UIInputViewController {
#IBOutlet var swipeButton: UIButton!
let swipeRec = UISwipeGestureRecognizer()
override func viewDidLoad() {
super.viewDidLoad()
self.loadInterface()
var swipeButtonDown: UISwipeGestureRecognizer = UISwipeGestureRecognizer(target: self, action: "ButtonDown")
swipeButtonDown.direction = UISwipeGestureRecognizerDirection.Down
self.view.addGestureRecognizer(swipeButtonDown)
}
#IBAction func buttonPressed(sender: UIButton) {
var proxy = textDocumentProxy as UITextDocumentProxy
proxy.insertText("button")
}
func buttonDown(){
var proxy = textDocumentProxy as UITextDocumentProxy
proxy.insertText("swipe")
}
}

If you want to add swipeGesture into Button then do it like this way:
self.yourButton.addGestureRecognizer(swipeButtonDown)
and also there is a mistake in selector you sent it should be like:
var swipeButtonDown: UISwipeGestureRecognizer = UISwipeGestureRecognizer(target: self, action: "buttonDown")
change ButtonDown to buttonDown

Related

How to create just one tapGestureRecognizer for several UILabels?

I am trying (and failing) to create just one tapGestureRecognizer to use on several UILabels.
Right now I am creating for every label in viewDidLoad a separate tapGestureRecognizer and adding it to the appropriate label. I ran into this problem because every touch should obviously call a different function.
This is how I create them:
#IBOutlet weak var buttonOne: UILabel!
#IBOutlet weak var buttonTwo: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
//tapGestureRecognizer for buttonOne
buttonOne.isUserInteractionEnabled = true
let oneGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(MainViewController.buttonOneAction))
buttonOne.addGestureRecognizer(oneGestureRecognizer)
//tapGestureRecognizer for buttonTwo
buttonTwo.isUserInteractionEnabled = true
let twoGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(MainViewController.buttonTwoAction))
buttonTwo.addGestureRecognizer(twoGestureRecognizer)
...
They work fine, but how and where could I create just one tapGestureRecognizer and add it in viewDidLoad to each label with a different action?
Just create one per label.
The alternative is to create a single tap gesture recognizer that you attach to the common superview of all target views, and write a BUNCH of code that does hit-testing to figure out if the tap landed on any of your labels, and if so, which one, and dispatches the desired method for that label.
However, that's the whole point of tap gesture recognizers. You'd probably spend several days developing a bunch of code that has no benefits over using multiple tap gesture recognizers.
As already mentioned in comments by #Duncan C you can switch the gesture recognizer view as follow:
class ViewController: UIViewController {
#IBOutlet weak var buttonOne: UILabel!
#IBOutlet weak var buttonTwo: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
[buttonOne, buttonTwo].forEach {
$0?.isUserInteractionEnabled = true
$0?.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(tapGesture)))
}
}
#objc func tapGesture(_ gesture: UITapGestureRecognizer) {
guard let label = gesture.view as? UILabel else { return }
switch label {
case buttonOne: print("buttonOne")
case buttonTwo: print("buttonTwo")
default: break
}
}
}

Tapping a view in xcode / swift 3 to 'select' it

I'm looking to hone in on the methodology used to 'select' UIViews by a single tap. In my UI, I have several UIViews that I would like to be 'selectable.' While selected, I will have controls to perform basic adjustments such as alpha (to let the user know which view is selected), and slightly more advanced functionality such as revealing a unique set of menu items. Only 1 view may be selected at a time. No multi-touch here.
The problem is, I've only seen this done with UICollectionViewCells. My interface isn't utilizing a collection view, so that's out of the question.
Thought process going into this is 1 tap gesture to 'select' the uiview, and then 'deselecting' it by tapping outside of the views bounds.
Another thought process is going with UIButtons instead of UIViews to utilize the Selected state.
Which would be the more viable solution here? Is this even possible using just UIViews? Or should I backtrack and go UIButtons instead?
Thanks.
You need to add a gesture recognizer to the desired UIView
class ViewController: UIViewController, UIGestureRecognizerDelegate {
#IBOutlet weak var myView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
let tap = UITapGestureRecognizer(target: self, action: #selector(self.tap(_:)))
tap.delegate = self
myView.addGestureRecognizer(tap)
}
}
Then you need to add the Code to handle whatever happens to your UIView into the gesture recognizer function:
func tap(_ gestureRecognizer: UITapGestureRecognizer) {
myView.alpha = 0.5
}
This will change UIView's alpha property to 0.5 once the UIView gets selected.
Now you could for example put a second UIView with a gesture recognizer and play with the alphas:
class ViewController: UIViewController, UIGestureRecognizerDelegate {
#IBOutlet weak var myView: UIView!
#IBOutlet weak var secondView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
let tap = UITapGestureRecognizer(target: self, action: #selector(self.tap(_:)))
tap.delegate = self
myView.addGestureRecognizer(tap)
let tap2 = UITapGestureRecognizer(target: self, action: #selector(self.tap2(_:)))
tap.delegate = self
secondView.addGestureRecognizer(tap2)
}
func tap(_ gestureRecognizer: UITapGestureRecognizer) {
myView.alpha = 0.5
secondView.alpha = 1
}
func tap2(_ gestureRecognizer: UITapGestureRecognizer) {
myView.alpha = 1
secondView.alpha = 0.5
}
}
That way, depending on the UIView you hit, the alpha will change...
Edit:
In this Code the UIViews will be "selected" - the alpha will change and will change back within a set timing of 0.8 seconds.
class ViewController: UIViewController, UIGestureRecognizerDelegate {
#IBOutlet weak var myView: UIView!
#IBOutlet weak var secondView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
let tap = UITapGestureRecognizer(target: self, action: #selector(self.tap(_:)))
tap.delegate = self
myView.addGestureRecognizer(tap)
let tap2 = UITapGestureRecognizer(target: self, action: #selector(self.tap2(_:)))
tap.delegate = self
secondView.addGestureRecognizer(tap2)
}
func tap(_ gestureRecognizer: UITapGestureRecognizer) {
myView.alpha = 0.5
UIView.animate(withDuration: 0.8, animations: {
self.myView.alpha = 1
})
}
func tap2(_ gestureRecognizer: UITapGestureRecognizer) {
secondView.alpha = 0.5
UIView.animate(withDuration: 0.8, animations: {
self.secondView.alpha = 1
})
}
}

Why isn't my UIBarButtonItem calling my custom function?

I have a UIViewController with a UIBarButtonItem configured to call my custom function called func settingsTapped(sender: AnyObject?) in which I've put performSegueWithIdentifier. I've determined that this function isn't being called even though the button DOES work and the segue somehow still works because it goes to the correct view controller.
First VC:
class CalculatorViewController: UIViewController {
private let timeInterval = 0.016 //how frequently we change the displayed number
private let animationLength = 0.3
private var timer: NSTimer?
private var counter: Int = 0
private var max: Double = 0.0
var startingPreferredUnits: String?
#IBOutlet weak var weightLifted: UITextField!
#IBOutlet weak var repetitions: UITextField!
#IBOutlet weak var oneRepMax: UILabel!
#IBOutlet weak var percentages: UITextView!
#IBOutlet weak var units: UILabel!
override func viewDidLoad() {
let settingsButton = UIBarButtonItem(title: "Settings", style: .Plain, target: self, action: #selector(settingsTapped(_:)))
self.navigationItem.leftBarButtonItem = settingsButton
super.viewDidLoad()
}
func settingsTapped(sender: AnyObject?) {
startingPreferredUnits = UserDefaultsManager.sharedInstance.preferredUnits
print("In segue, units is \(startingPreferredUnits)") // never prints this caveman debugging
self.performSegueWithIdentifier("segueToSettings", sender: self)
}
}
In the storyboard, I placed a Bar Button Item in the nav bar:
I created a Show (e.g. Push) segue between the Settings bar button item and the Settings view controller and I've given this segue an identifier of 'segueToSettings`. When I touch the Settings button, it does present the Settings view controller, but it doesn't print my caveman debugging line to the console.
I also tried creating the segue between the CalculatorViewController itself and the SettingsViewController (which I think may even be a better way) but when I have it set up that way, nothing happens at all when I touch the Settings button.
I've tried everything I could find on SO but nothing has worked. I hope I don't earn a Stupid Question badge on this one.
UPDATE 1:
I'm still struggling with this one. Here's something else I've learned and tried that didn't work. Clicking the Settings button works by performing the segue from the button to the Settings page which I created in the Storyboard. As one would expect, it will call override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) before doing this. So it's completely ignoring the action: #selector(settingsTapped(_:)) part. Also, I can change the identifier of the segue in the storyboard and it makes no difference at all. It still works. I can even delete the identifier and it works.
I also tried adding another button (this time a barButtonSystemItem like so:
override func viewDidLoad() {
super.viewDidLoad()
let settingsButton = UIBarButtonItem(title: "Settings", style: .Plain, target: self, action: #selector(settingsTapped(_:)))
let saveButton = UIBarButtonItem(barButtonSystemItem: .Add, target: self, action: #selector(self.saveThisLift(_:)))
self.navigationItem.leftBarButtonItem = settingsButton
self.navigationItem.rightBarButtonItem = saveButton
}
func saveThisLift(sender: UIBarButtonItem) {
print("I'm in saveThisLift") // never prints
let weight = weightLifted.text
let reps = repetitions.text
let maxAmount = oneRepMax.text
let unitsText = units.description
coreDataStack.saveLiftEvent(currentLiftName!, formula: currentFormulaName!, weight: weight!, repetitions: reps!, maxAmount: maxAmount!, unitsUsed: unitsText)
}
func settingsTapped(sender: AnyObject?) {
startingPreferredUnits = UserDefaultsManager.sharedInstance.preferredUnits
print("In segue, units is \(startingPreferredUnits)") // never prints this caveman debugging
self.performSegueWithIdentifier("segueToSettings", sender: self)
}
Like the Settings button, touching it does nothing except make it flash. I should point out that I added the bar button items via the storyboard and I think that's unnecessary since I'm trying to add them programmatically. However, without them, neither of the buttons appears.
After a lot more research and more trial and error, I've figured it out. I learned a number of things that I'll leave here for anyone who has this problem in the future.
At a high-level, trying to do some of this in the storyboard and some in code made it easy to get confused. The key things for me were:
I wasn't dealing with a UINavigationController with its out-of-the-box root view controller, I was dealing with a UIViewController. With the UINavigationController, you don't have to do as much. But with a UIViewController, I had to add a UINavigationBar and to that, I had to add a single UINavigationItem. I did this by dragging them from the Object Library to my storyboard.
Don't try to put a UILabel in the UINavigationBar to do what I was trying to do, which was have them call custom functions. Trust me, it doesn't work, at least not in my case.
After figuring out I needed to add a UINavigationItem, the next ah-ha! moment for me was the fact that I could put multiple UIBarButtonItems in it (note: my example in my question shows just one to keep it simple but I'm actually adding three items)
The magic piece of the puzzle was connecting my code to the storyboard. I simply Ctrl-clicked from my UINavigationItem to my view controller and created an #IBOutlet called #IBOutlet weak var navItem: UINavigationItem!
My code that creates the buttons and adds them to the view uses this outlet like so (simplified):
_
class CalculatorViewController: UIViewController {
#IBOutlet weak var navItem: UINavigationItem!
override func viewDidLoad() {
super.viewDidLoad()
let settingsButton = UIBarButtonItem(title: "Settings", style: .Plain, target: self, action: #selector(self.segueToSettings(_:)))
let viewLogButton = UIBarButtonItem(title: "Log", style: .Plain, target: self, action: #selector(self.segueToLog(_:)))
let saveButton = UIBarButtonItem(barButtonSystemItem: .Add, target: self, action: #selector(self.saveLift(_:)))
self.navItem.leftBarButtonItem = settingsButton
self.navItem.rightBarButtonItems = [saveButton, viewLogButton]
}
func saveLift(sender: AnyObject) {
let weight = weightLifted.text
let reps = repetitions.text
let maxAmount = oneRepMax.text
let unitsText = units.text
coreDataStack.saveLiftEvent(currentLiftName!, formula: currentFormulaName!, weight: weight!, repetitions: reps!, maxAmount: maxAmount!, unitsUsed: unitsText!)
performSegueWithIdentifier("segueToLog", sender: self)
}
}
Lastly, when you create that #IBOutlet, don't name it navigationItem: UINavigationItem because that will make Xcode very unhappy:
I burned a lot of hours on this one. I hope this information helps somebody avoid that in the future.

Double tap gesture function is being fired after 1 tap only on first tap, every other instance it requires 2 taps

As of right now, when I run my program and tap, I am getting the println() message after one tap. However, after the first println() message, I have to double tap for every following println() message.
I want it so every time I have to double tap (including the first time).
In my View Controller I have the following:
#IBOutlet weak var graphview: GraphView! {
didSet {
//
graphview.addGestureRecognizer(UITapGestureRecognizer(target: graphview, action: "doubleTap:"))
}
}
And my function in the View is the following:
func doubleTap(gesture: UITapGestureRecognizer)
{
gesture.numberOfTapsRequired = 2
println("hit twice")
}
You have to set
gesture.numberOfTapsRequired = 2
before adding the gesture recognizer to the view, i.e.
#IBOutlet weak var graphview: GraphView! {
didSet {
var doubleTap: UITapGestureRecognizer = UITapGestureRecognizer(target: graphview, action: "doubleTap:")
doubleTap.numberOfTapsRequired = 2
graphview.addGestureRecognizer(doubleTap)
}
}
and
func doubleTap(gesture: UITapGestureRecognizer)
{
println("hit twice")
}

Closed:: Creating a UIButton horizontal carousel with swipe and tap gestures

Inside of a VC, I'm trying to figure out how to have a UIButton recognize a horizontal swipe and then tap.
There is an array of text.
var plotList = ["stuff", "to do", "this", "or that"]
default display is plotList[0].
the carousel repeats in an infinite loop doesn't stop at the end of the array.
as user swipes over button only, i don't want the other components of the VC to react to this particular swipe, i want the button to display the plotList string.
when the user taps on the button, i want to shove the result into a switch that will launch the appropriate UIView.
i'd like to avoid doing this UILabel/UIButton combination. See Handling Touch Event in UILabel and hooking it up to an IBAction.
so far i'm here
import UIKit
class carouselVC: UIViewController {
#IBOutlet var carouselView: UIView!
#IBOutlet var labelOutlet: UILabel!
#IBOutlet var buttonOutlet: UIButton!
var plotList = ["stuff", "this", "that"]
let swipeRec = UISwipeGestureRecognizer()
override func viewDidLoad() {
super.viewDidLoad()
var swipeButtonLeft: UISwipeGestureRecognizer =
UISwipeGestureRecognizer(target: self, action: "buttonLeft")
swipeButtonLeft.direction = UISwipeGestureRecognizerDirection.Left
self.buttonOutlet.addGestureRecognizer(swipeButtonLeft)
var swipeButtonRight: UISwipeGestureRecognizer =
UISwipeGestureRecognizer(target: self, action: "buttonRight")
swipeButtonRight.direction = UISwipeGestureRecognizerDirection.Right
self.buttonOutlet.addGestureRecognizer(swipeButtonRight)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func buttonLeft(){
println("buttonLeft")
}
func buttonRight(){
println("buttonRight")
}
#IBAction func buttonAction(sender: UIButton) {
sender.setTitle(plotList[1], forState: .Normal)
}
}
any help?
Use a UIView with a UILabel, instead of button. Add UIView over UILabel and make it clear color. You may make UIlabel look like a button. Add a swipe gesture/tap gesture on it. And do any operation in the action of the gestures.

Resources