Getting UiView tag when touched - ios

I have developed a UIView from For loop and basically it is create 3 Views from loop. and I have to add touch gesture on every View to call a method but I am unable to get current selected UIView.tag when I tap on it. it is only showing the .tag of the last view. here is my code.
for i in 0 ... 2 {
let productView = UIView()
productView.tag = i
productView.isUserInteractionEnabled = true
let producttap = UITapGestureRecognizer(target: self, action: #selector(self.ProductTapped))
productView.addGestureRecognizer(producttap)
productView.frame = CGRect(x: xOffset, y: CGFloat(buttonPadding), width: 200, height: scView1.frame.size.height)
xOffset = xOffset + CGFloat(buttonPadding) + productView.frame.size.width
scView1.addSubview(productView)
productIndex = productView.tag
}
and here is the method that I am calling from every UIView touch.
#objc func ProductTapped() {
print("",productIndex)
}

Your code should be using delegate/callback closure, but if you want to keep using tag, try change it to:
#objc func ProductTapped(_ sender: UITapGestureRecognizer) {
if let view = sender.view {
print(view.tag)
}
}
and the gesture attach to let producttap = UITapGestureRecognizer(target: self, action: #selector(self.ProductTapped(_:)))
productIndex does nothing here since it got overwritten on the loop

productIndex currently has no relationship to the tap gestures that you attach your views. You do set productIndex in the the loop but that's irrelevant to your gesture.
Perhaps you want
let producttap = UITapGestureRecognizer(target: self, action: #selector(productTapped(_:))
and
#objc func productTapped(_ gesture: UITapGestureRecognizer) {
print("tag is",gesture.view.tag)
}

Related

How to detect longpress in BarButtonitem

I have a UIBarButtonItem in navigation bar. When a user clicks it, it pops to another viewController.
Now i want that when user long-press on that button (navigation bar button) I want to show a help message.
I want help to detect the onlick event and longpress event separately.
You should create a button and set UITapGestureRecognizer & UILongPressGestureRecognizer to your button
// Create a button
let yourButton = UIButton()
yourButton.backgroundColor = .red
yourButton.setTitle("long press", for: .normal)
// Create a tap gesture recognizer
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(didTap))
// Create a long gesture recognizer
let longGesture = UILongPressGestureRecognizer(target: self, action: #selector(long))
// You can set minimum duration of the press action
longGesture.minimumPressDuration = 3 //The default duration is 0.5 seconds.
// Add your gestures to button
yourButton.addGestureRecognizer(longGesture)
yourButton.addGestureRecognizer(tapGesture)
navigationItem.leftBarButtonItem = UIBarButtonItem(customView: yourButton)
#objc private func didTap() {
print("Did Tap")
}
#objc private func long() {
// You can show the help message in here
print("Long press")
}
try this in view didload:
let back = UIImage(named: "header_backarrow")
let backView = UIImageView(image: back)
backView.isUserInteractionEnabled = true
let tap = UITapGestureRecognizer(target: self, action: #selector(dismissManual))
backView.addGestureRecognizer(tap)
let backItem = UIBarButtonItem(customView: backView)
navigationItem.leftBarButtonItem = backItem
In the viewDidAppear of your view controller you can add this :
let gestureRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(myCalledFunction))
(myUIBarButton.value(forKey: "view") as? UIView)?.addGestureRecognizer(gestureRecognizer)
This is difficult because UIBarButton doesn't really expose its view and so you cannot directly add a gestureRecognizer to it.
You can however get a reference to its view by using the value(forKey:) method and then play with it.
Do not do this in the viewDidLoad as it is necessary for the view to have already been created in order for this to work.
All of the other answers require implementing a UIBarButtonItem(customView:) to achieve this. However this can be implemented with any UIBarButtonItem instance without implementing your own gesture recognizer code.
An #IBAction can actually be passed a second parameter containing the UIEvent triggering the action. For example, instead of defining -
#objc func doSomething(sender: UIBarButtonItem) {
}
We can define -
#objc func doSomething(sender: UIBarButtonItem, forEvent event: UIEvent) {
guard let touch = event.allTouches?.first else { return }
if touch.tapCount == 1 {
// Handle tap
} else if touch.tapCount == 0 {
// Handle long press
}
}
Source : http://li366-68.members.linode.com/2016/09/07/detecting-long-presses-on-uibarbuttonitems.html
so I found that UIBarButton has not property like longpress so all I do is take a UIButton give it longpress gesture and add that UIButton in navigation bar as UIBarButtonItem.
I hope it will helpful for someone else who is facing same problem.
let btn = UIButton(frame: CGRect(x: 0, y: 0, width: 40, height: 40))
btn.backgroundColor = .green
let gesture = UILongPressGestureRecognizer(target: self, action: #selector(longpress))
btn.addGestureRecognizer(gesture)
let barbtn = UIBarButtonItem(customView: btn)
self.navigationItem.rightBarButtonItem = barbtn
thank you :)

Getting Tag from Tap Gesture Recognizer

I've got an array of UIImageViews and have programmatically assigned tap gesture recognizers to them.
myImages.forEach{ UIImageView in
let tap = UITapGestureRecognizer(target: self, action: #selector(handleTap(gesture:)))
tap.numberOfTapsRequired = 1
tap.delegate = self
view.addGestureRecognizer(tap)
}
What's the best way to assign a sender to each (or determine which image was tapped another way)? I've unsuccessfully tried
var tag = sender.view!.tag
Thanks!
in here you need to follow two steps,
step 1
assign the tags for imageview before append to your myImages array.
step 2
get the tag from imageview array and assign to your each gesture
myImages.forEach{
let tap = UITapGestureRecognizer(target: self, action: #selector(handleTap(_:)))
tap.numberOfTapsRequired = 1
tap.view?.tag = $0.tag
$0.isUserInteractionEnabled = true
$0.addGestureRecognizer(tap)
}
and handle the func like
#objc func handleTap(_ sender: UITapGestureRecognizer) {
guard let getTag = sender.view?.tag else { return }
print("getTag == \(getTag)")
}
You can use the block provided by UITapGestureRecognizer init to access your images in place.
myImages.forEach { image in
let tap = UITapGestureRecognizer(block: {[weak self] _ in
//Do your stuff here
//print("Image Tapped:", image.debugDescription)
}, delegate: self)
tap.numberOfTapsRequired = 1
image.addGestureRecognizer(tap)
}
If you want to set UITapGestureRecognizer in UICollectionView or UITableView cell then below solution is useful for us.
Step 1 Assign the UITapGestureRecognizer to particuller textview or other view in UICollectionView or UITableView cell.
cell.textView?.delegate = self
cell.textView?.isEditable = false
cell.textView?.isSelectable = true
let tap = UITapGestureRecognizer(target: self, action:#selector(self.onclickLink(_:)))
cell.textView?.tag = indexPath.row
tap.numberOfTapsRequired = 1
cell.textView?.addGestureRecognizer(tap)
Step 2 Get the tag from UITextView or other View in onclick action.
#IBAction func onclickLink(_ sender: UITapGestureRecognizer) {
print("indexPathRow == \(sender.view?.tag ?? 0)")
}

Referring to programmatically created UI elements

I'm creating many UI elements programmatically. Let's say 50 UILabels.
What is a proper way to access to labels properties?
Now i'm adding a tag to each label and next search label in [subview] array and get label properties through a 'sender':
func buttonTapped(sender: UIButton) {
for subview in containerView.subviews {
if let label = subview as? UILabel, label.tag == sender.tag {
// do stuff
}
}
}
Not sure it's most elegant way because there are some problems if we change labels to buttons.
Lets say:
func createButton() {
let button = UIButton(frame: CGRect(origin: ...,
size: ...))
button.addTarget(self, action: #selector(buttonTapped(sender:)), for: .touchUpInside)
let longTap = UILongPressGestureRecognizer(target: self, action: #selector(disableButton(sender:)))
longTap.minimumPressDuration = 1
button.addGestureRecognizer(longTap)
.......
containerView.addSubview(button)
}
And now i can't access to properties via 'sender' in disableButton(sender:) method, because sender is UILongPressGestureRecognizer.
Seems like im doing something wrong if it works for labels but not for buttons.
Please, guide me in right direction
First of all, I would keep and array with a reference to all of the labels so you don't have to iterate through all of the subviews to find them and keep this in order with the highest z value first. But if you don't want to do that here is the code that can find you view in one line; its not the most efficient thing in the world but for your number fo views it should be fine.
class ViewController: UIViewController {
let first = UILabel(frame: CGRect(x: 0, y: 0, width: 100, height: 50))
let second = UILabel(frame: CGRect(x: 25, y: 25, width: 100, height: 50))
override func viewDidLoad() {
first.text = "first"
first.backgroundColor = .blue
second.text = "second"
second.backgroundColor = .red
view.addSubview(first)
view.addSubview(second)
let tap = UITapGestureRecognizer(target: self, action: #selector(handleTap(sender:)))
view.addGestureRecognizer(tap)
}
#objc func handleTap(sender: UITapGestureRecognizer) {
if let label = view.subviews.flatMap({$0 as? UILabel}).last(where: { $0.bounds.contains(sender.location(in: $0))}) {
print("You pressed: \(label)")
}
}
}
Note whats going on here:
We use flatMap to discard any subviews that are not UIlabels.
We use last to find the last item in the array (
because the top most views are last according to UIView.subviews) where the touch is inside that view's bounds.
Simple add this code when your creating your label
yourLbl.isUserInteractionEnabled = true
And use touchesBegan method to get your clicked label
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch = touches.first
if let lblView = touch?.view
{
if lblView.isKind(of: UILabel.self)
{
let yourLbl = lblView as! UILabel
//Access your clicked lable
}
}
}

Pass extra argument for UItapgestureRecognizer with selector

I have two labels, Label1 and Label2. I want to make a single function that prints out which label is tapped by creating UITTapRecognizer for both labels calling the same function with selector that passes an argument. Below is the long way of doing it which is messy but works. If I know how to pass an argument (Int) into the selector, it would be alot cleaner.
let topCommentLbl1Tap = UITapGestureRecognizer(target: self, action: #selector(DiscoverCell().doubleTapTopComment1))
topCommentLbl1Tap.numberOfTapsRequired = 2
topCommentLbl1.userInteractionEnabled = true
topCommentLbl1.addGestureRecognizer(topCommentLbl1Tap)
let topCommentLbl2Tap = UITapGestureRecognizer(target: self, action: #selector(DiscoverCell().doubleTapTopComment2))
topCommentLbl2Tap.numberOfTapsRequired = 2
topCommentLbl2.userInteractionEnabled = true
topCommentLbl2.addGestureRecognizer(topCommentLbl2Tap)
func doubleTapTopComment1() {
print("Double Tapped Top Comment 1")
}
func doubleTapTopComment2() {
print("Double Tapped Top Comment 2")
}
Is there a way to modify the selector method such that I can do something like
func doubleTapTopComment(label:Int) {
if label == 1 {
print("label \(label) double tapped")
}
Short answer: no
The selector is called by the UITapGestureRecognizer, and you have no influence on what parameters it passes.
However, what you can do is query the recognizer's view property to get the same information.
func doubleTapComment(recognizer: UIGestureRecognizer) {
if recognizer.view == label1 {
...
}
else if recognizer.view == label2 {
...
}
}
Provide both gesture recognizers with the same selector that takes a single parameter. That action method will be passed instance of a UIGestureRecognizer, and happily, that gesture recognizer has a property called view, which is the view to which the gr is attached.
... action: #selector(doubleTapTopComment(_:))
func doubleTapTopComment(gestureRecognizer: gr) {
// gr.view is the label, so you can say gr.view.text, for example
}
I believe a UITapGestureRecognizer can only be used for a single view. That said, you can have 2 different UITapGestureRecognizers call the same selector and then access the UITapGestureRecognizer in the function. See the following code:
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let label1 = UILabel()
label1.backgroundColor = UIColor.blueColor()
label1.frame = CGRectMake(20, 20, 100, 100)
label1.tag = 1
label1.userInteractionEnabled = true
self.view.addSubview(label1)
let label2 = UILabel()
label2.backgroundColor = UIColor.orangeColor()
label2.frame = CGRectMake(200, 20, 100, 100)
label2.tag = 2
label2.userInteractionEnabled = true
self.view.addSubview(label2)
let labelOneTap = UITapGestureRecognizer(target: self, action: #selector(ViewController.whichLabelWasTapped(_:)))
let labelTwoTap = UITapGestureRecognizer(target: self, action: #selector(ViewController.whichLabelWasTapped(_:)))
label1.addGestureRecognizer(labelOneTap)
label2.addGestureRecognizer(labelTwoTap)
}
Both UITapGestureRecognizers call the same function:
func whichLabelWasTapped(sender : UITapGestureRecognizer) {
//print the tag of the clicked view
print (sender.view!.tag)
}
If you try to add one of the UITapGestureRecognizers to both labels, then only the last one added will actually call the function.
You can do like this,
let topCommentLbl1Tap = UITapGestureRecognizer(target: self, action: #selector(labelTapped(_:)))
let topCommentLbl2Tap = UITapGestureRecognizer(target: self, action: #selector(labelTapped(_:)))
label1.addGestureRecognizer(topCommentLbl1Tap)
label2.addGestureRecognizer(topCommentLbl2Tap)
#objc
func textViewTapped(_ sender: UITapGestureRecognizer) {
if(sender.view.tag == label1.tag) {
print("I am label1")
} else if(sender.view.tag == label2.tag) {
print("I am label2")
}
}
don't forgot to set tags to labels.

How do I pass a number to a gesture recognizer action?

I have the next code
var i = 0
for answer in answeres{
let singleTap = UITapGestureRecognizer(target: self, action:#selector(tapDetected(_:))
imageView.addGestureRecognizer(singleTap)
i+=1
}
func tapDetected(position : Int) {
print(position)
}
How can I pass the var 'i' to each imageView, so when the user click the imageView, it prints the correct number in the log?
The method called by the recognizer is passed the recognizer as a first argument:
func tapDetected(sender: UITapGestureRecognizer) {}
Which means you can use the view property of the recognizer to access the view associated with it. And the number can be stored as the view tag:
var i = 0
for answer in answers {
let singleTap = …
let imageView = …
imageView.addGestureRecognizer(singleTap)
imageView.tag = i
i = i + 1
}
func tapDetected(sender: UITapGestureRecognizer) {
print(sender.view.tag)
}

Resources