I'm creating a resume app (mostly for fun) and can't seem to get the tap gesture recognizer to work for me. What I'd like is to have a single label display the information and have it change depending on which title label they tap below. Here is the code I've written:
import UIKit
class WorkHistoryViewController: UIViewController {
// MARK: Properties
#IBOutlet weak var jobOne: UILabel!
#IBOutlet weak var jobTwo: UILabel!
#IBOutlet weak var jobThree: UILabel!
#IBOutlet weak var jobFour: UILabel!
#IBOutlet weak var jobFive: UILabel!
#IBOutlet weak var workHistoryDescriptionLabel: UILabel!
let tapRec = UITapGestureRecognizer()
override func viewDidLoad() {
super.viewDidLoad()
tapRec.addTarget(self, action: "tappedLabel")
jobOne.addGestureRecognizer(tapRec)
jobTwo.addGestureRecognizer(tapRec)
jobThree.addGestureRecognizer(tapRec)
jobFour.addGestureRecognizer(tapRec)
jobFive.addGestureRecognizer(tapRec)
}
// MARK: Methods
func tappedLabel() {
workHistoryDescriptionLabel.text = "It worked!"
}
}
What's happening is that the last label to have addGestureRecognizer() called in viewDidLoad() is the only one that works. If I comment out the last line then only the label above it works. I also tried to enable user interaction on each of the labels programmatically and on the attributes inspector and neither changed anything.
As per Apple's Event Handling Guidelines
Gesture Recognizers Are Attached to a View
Every gesture recognizer is
associated with one view. By contrast, a view can have multiple
gesture recognizers, because a single view might respond to many
different gestures. For a gesture recognizer to recognize touches that
occur in a particular view, you must attach the gesture recognizer to
that view. When a user touches that view, the gesture recognizer
receives a message that a touch occurred before the view object does.
As a result, the gesture recognizer can respond to touches on behalf
of the view.
So you need to create multiple instances of UITapGestureRecognizer and attach them to each view, even if they perform same actions.
For instance,
let tapRecOne = UITapGestureRecognizer()
tapRecOne.addTarget(self, action: "tappedLabel")
jobOne.addGestureRecognizer(tapRecOne)
let tapRecTwo = UITapGestureRecognizer()
tapRecTwo.addTarget(self, action: "tappedLabel")
jobTwo.addGestureRecognizer(tapRecTwo)
and so on.
You need to create new object of tap gestures to get your work done.You can refer showing images in UIScrollview horizontally on ipad and getting tag of image on tap
Here is Apple's documentation as defined here.
A gesture recognizer operates on touches hit-tested to a specific view and all of that view’s subviews. It thus must be associated with that view. To make that association you must call the UIView method addGestureRecognizer:. A gesture recognizer doesn’t participate in the view’s responder chain.
That means that you need to have separate "UIGestureRecognizer" instances for each view. That should sort out the issue.
Xcode has a small icon in Xcode you see in this screen shot that you can click on when your app is running. As I put in this screenshot its invaluable when trying to understand what is happening with UI issues?
Related
I have two copies of a control (its called a RatingControl). How do I write handlers that can be invoked on the correct object when somebody double taps on them?
I have:
#IBOutlet weak var ratingControl: RatingControl!
#IBOutlet weak var ratingControl2: RatingControl!
inside a TableViewController and then
override func viewDidLoad() {
super.viewDidLoad()
let tapGR = UITapGestureRecognizer(target: ratingControl, action: #selector(RatingControl.doubleTap(_:)))
tapGR.numberOfTapsRequired = 2
self.view.addGestureRecognizer(tapGR)
let tapGR2 = UITapGestureRecognizer(target: ratingControl2, action: #selector(RatingControl.doubleTap(_:)))
tapGR2.numberOfTapsRequired = 2
self.view.addGestureRecognizer(tapGR2)
}
RatingControl.doubleTap(_) is an innocuous event handler.
When there is a double tap on the second rating control, the doubleTap method is called but is dispatched on the first rating control object!
I have tried setting two targets on a single UITapGestureRecognizer but it runs into the same problem.
Thanks much!
You need to add the gesture recognizers to the two rating controls instead of to self.view.
Try this:
let tapGR = UITapGestureRecognizer(target: self, action: #selector(RatingControl.doubleTap(_:)))
tapGR.numberOfTapsRequired = 2
ratingControl.addGestureRecognizer(tapGR) // ratingControl, not self.view
let tapGR2 = UITapGestureRecognizer(target: self, action: #selector(RatingControl.doubleTap(_:)))
tapGR2.numberOfTapsRequired = 2
ratingControl2.addGestureRecognizer(tapGR2) // ratingControl2, not self.view
There are 2 parts to hooking up a gesture recognizer: the target, which determines which object gets notified when the recognizer is triggered, and the view it's attached to, which determines from which view the recognizer recognizes the gesture.
You've got 2 gesture recognizes configured the same way, both attached to self.view. They are therefore going to respond to taps on self.view (which I assume is the view controller's content view.) I don't think it's clear which gesture recognizer is going to be triggered when you tap in that case.
You should have 2 different views and attach a different gesture recognizer to each one. If ratingControl1 and ratingControl2 are view objects, perhaps you meant to attach the gesture recognizers directly to them, rather than to self.view?
I'm sure that answer is near.
I'm new in swift and want to create simple app for iOS.
I have created view and add different elements on this view. Also added pan recognized for this view. I want to do different actions when user drag his finger over different element. For example when finger entered in button area to write in console, when dragged into label area - change labels's text. Also With this type realized swipe method of input text from keyboard. It must do in one touch that's why I use pan recognizer. But when I run my app, I can get finger position, but don't know how triggered function when need.
Hope for yours help
Thanx
class ViewController: UIViewController {
#IBOutlet var mainView: UIView!
#IBOutlet var gest: UIPanGestureRecognizer!
#IBOutlet var tap: UITapGestureRecognizer!
#IBOutlet weak var btn: UIButton!
#IBOutlet weak var btn2: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.view.addGestureRecognizer(self.gest)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func drAG(_ sender: UIPanGestureRecognizer) {
print(sender.location(in: self.view))
}
Looks like you have most everything you need. Now, just use the hitTest() method of any view's layer:
func moveEye(_ recognizer:UIPanGestureRecognizer) {
let p = recognizer.location(in: self)
if btn.layer.hitTest(p) != nil {
// finger panned over btn
}
}
I use hitTest() alot in a few apps. But beware - I usually see it associated with the tap gesture. (I use the pan gesture to move a subview.) I do not know how things will behave if you have two overlapping subviews that the user pans over.
I have a tap gesture on a UIImageView within a class that extends UITableViewCell. This code should work, I don't see why it doesn't. The only thing I am iffy on is what the "target" should be - should it be the profileImage, or the overall ViewController that things are in?
#IBOutlet weak var profileImage: UIImageView!
var vc: TweetsViewController? = nil
override func awakeFromNib() {
super.awakeFromNib()
let tapGester = UITapGestureRecognizer(target: vc, action: Selector("handleTapGester:"))
tapGester.delegate = self
profileImage.addGestureRecognizer(tapGester)
}
func handleTapGester(tapGesture: UITapGestureRecognizer) {
print("*******hi*******")
vc?.performSegueWithIdentifier("showProfile", sender: nil)
}
And for the record, as this may seem like a relevant error, I initialize vc when the table cell loads.
The target should be the object that will handle the tap gesture and the handleTapGester function should be inside the object class you specified as the target, not inside the UITableViewCell subclass.
You also need to enable user interaction on the UIImageView by saying:
imageView.userInteractionEnabled = true
Why not just add a tap gesture recogniser to the view, then when called query indexPathForRowAtPoint to find out which cell is being tapped?
If you know the cell you can then determine if the UIImage is being tapped and make your call to performSegueWithIdentifier from there.
If it's tapped on a cell that you're not interested in let it fall through and be handled by the table by calling cancelsTouchesInView on the recogniser.
As far as i observed, Tap gestures cancels event when finger moves. Here a screenShot from Event Handling Guide:
I have already done some tests with the following code. Nope, it cancels...
#IBAction func dosomething(sender: UITapGestureRecognizer) {
//called by gesture recognizer
}
#IBOutlet var red: UIView!
#IBOutlet var green: UIView!
#IBOutlet var yellow: UIView!
#IBOutlet var white: UIView!
//setting delegate when outlet get set.
#IBOutlet var tapGesture: UITapGestureRecognizer! { didSet { outlet.delegate = self
}}
//delegate func shouldReceiveTouch
func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldReceiveTouch touch: UITouch) -> Bool {
if touch.view == red {
println("ez")
return true
}else if touch.view == green{
println("bp")
return false
}else { view.backgroundColor = UIColor.clearColor()
return true}
}
So if I'm right, when touchesMoved: called, tapGestureRecognizer must have failed, but according to figure it does not. What I'm missing here?
When a user taps or drags their finger across a screen in an iPhone application, two things happen:
The view receives a UITouch object(s)
A UIGestureRecognizer may grab the UITouch object(s) and try to infer what the user is doing (pan in/out, scroll up/down, swipe between pages in a browser, etc.)
A UIGestureRecognizer will not know what the user is doing unless the user has finished tapping the screen. The iPhone will know when the user is done when the UITouch object(s) are no longer apart of the current view.
The thing is, UIGestureRecognizer doesn't "fail", but rather it has no value or meaning until the user's touches are "cancelled" or not apart of the view. This can be seen by the fourth column to the right of Figure 1-6. Another problem is that a tap is not what you think it is. It's just a single touch. The touch ends as soon as it begins. If you're trying to swipe or drag something across a screen, you need a different UIGestureRecognizer object such as UIPanGestureRecognizer, UILongPressGestureRecognizer, UISwipeGestureRecognizer, etc.
If you want to see this in action, try doing the following in an iOS Single View Application:
Make a ViewController.swift file.
Class your ViewController as a subclass of UIViewController.
Add a UIGestureRecognizerDelegate protocol.
Add some UITapGestureRecognizer objects and UITouch objects and link them to a single view in your storyboard.
Set the UIGestureRecognizer object's delegate to the UIViewController or self.
Check when a touch begins and ends by adding some println statements in the touchesBegan:withEvent:, touchesMoved:withEvent:, touchesEnded:withEvent, and touchesCancelled:withEvent methods.
You can learn more about how these methods work in the iOS Developer Library directly.
I have a webview inside view controller "A", is it possible to tap on web view and seaque to view controller "B"?
I have tried do following
#IBOutlet weak var mainWebView: UIWebView!
let tapUIWebView = UITapGestureRecognizer()
override func viewDidLoad() {
super.viewDidLoad()
tapUIWebView.addTarget(self, action: "webViewTapped")
mainWebView.userInteractionEnabled = true
mainWebView.addGestureRecognizer(tapUIWebView)
func webViewTapped(){
println("Tapped")
}
}
Thank you
1) Use a UITapGestureRecognizer, create the instance.
2) Attach it to the webview.
3) Implement the action method that handles the gesture.
4) SinceUIWebView already recognises a touch, you need to also take care that your recognizer does the job first. And you decide what to do with the touch.
5) If it suits you, you can disable handling the tap in the built-in recogniser completely.