UITapGestureRecognizer not working for specific [UIView] array - ios

I have the following piece of code. It's a third party library for a menu (named CarbonKit). When I try to select a specific segment (tab) and add a gesture recognizer, it doesn't work. Any ideas what I'm doing wrong?
To be clear, I placed a breakpoint in the handleTap, it it doesn't even enter the function.
override func viewDidLoad() {
super.viewDidLoad()
self.view.userInteractionEnabled = true
let tgr : UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(OverviewFolder.handleTap(_:)))
// segment 2 (categories)
carbonTabSwipeNavigation.carbonSegmentedControl?.segments![2].userInteractionEnabled = true
carbonTabSwipeNavigation.carbonSegmentedControl?.segments![2].addGestureRecognizer(tgr)
}
// tap
func handleTap(gestureRecognizer : UITapGestureRecognizer){
let test = carbonTabSwipeNavigation.currentTabIndex
if test == 2 {
print("second item tapped")
}
}

If the 3rd party UISegmentedControl is like the generic one, you already have everything you need. Here's some of my code. If you are using IB, wire the control up to an IBAction instead.
let imageSegments = UISegmentedControl (items: ["Original","Stained"])
override func viewDidLoad() {
super.viewDidLoad()
imageSegments.tintColor = UIColor.yellow
imageSegments.selectedSegmentIndex = 1
imageSegments.addTarget(self, action: #selector(changeImageView), for: .valueChanged)
view.addSubview(imageSegments)
}
func changeImageView() {
switch imageSegments.selectedSegmentIndex {
case 0:
imageView.image = imgOriginal
case 1:
imageView.image = imgEdited
default:
break
}
imageView.setNeedsDisplay()
}

Related

How do I identify where the click of a UILabel came from? [duplicate]

This question already has answers here:
In Swift is there anyway to know which object was swiped?
(2 answers)
How to detect which image has been tapped in swift
(1 answer)
Closed 3 years ago.
In my program, when the "newFrees" function is run, it should activate the ability to click on some labels. Depending on which label the user clicks, it should do something different. I've gotten to the point where I can make it run a function when one of the labels is pressed, but I don't know how to make that function identify which label called it. Is there a way I could identify the name of the label that called it?
func newFrees() -> Void {
BM1.isUserInteractionEnabled = true
BT2.isUserInteractionEnabled = true
let tap1 = UITapGestureRecognizer(target: self, action: #selector(ViewController.tapFunction))
let tap2 = UITapGestureRecognizer(target: self, action: #selector(ViewController.tapFunction))
BM1.addGestureRecognizer(tap1)
BT2.addGestureRecognizer(tap1)
}
...
#objc func tapFunction(sender:UITapGestureRecognizer) {
print("tap working")
}
// BM1 and BT2 are labels.
Also, is there a way to not have to use "let tap1' and "let tap2"? Whenever I just define it as "tap" and then add it to both BM1 and BT2, BT2 works, but not BM1.
You can try using tags like this....
func newFrees() -> Void {
BM1.isUserInteractionEnabled = true
BT2.isUserInteractionEnabled = true
BTM1.tag = 1
BTM2.tag = 2
let tap1 = UITapGestureRecognizer(target: self, action: #selector(ViewController.tapFunction))
BM1.addGestureRecognizer(tap1)
BT2.addGestureRecognizer(tap1)
}
#objc func tapFunction(sender:UITapGestureRecognizer) {
print("tap working")
print("tap working")
guard let label = sender.view as? UILabel else { return }
switch label.tag {
case 1:
//DO stuff with label 1
case 2:
//Do Stuff with label 2
default:
break
}
}
}
You can try
#objc func tapFunction(sender:UITapGestureRecognizer) {
if bm1 == sender.view {
print("bm1 clicked")
}
else {
print("bt2 clicked")
}
}
You have to create a tap instance for each object , creating 1 and adding it to 2 or many objects will be active to the last object it added to
func addTap(_ lbl:UILabel) {
lbl.isUserInteractionEnabled = true
lbl.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(ViewController.tapFunction)))
}

UITapGestureRecognizer to hide and unhide the label data with swift4

I need UITapGestureRecognizer to both hide and unhide the value. User with single tap should hide the label value and with the single tap should unhide the label, is there any way I could perform this operation?
Now I have used tap and long-tap gesture to perform this operation. Below is my code,
let tab = UITapGestureRecognizer(target: self, action: #selector(availabelBalance))
tab.numberOfTapsRequired = 1
tab.cancelsTouchesInView = false
accountBalanceView.addGestureRecognizer(tab)
let tabTwo = UILongPressGestureRecognizer(target: self, action: #selector(availabelBalanceTwo))
accountBalanceView.addGestureRecognizer(tabTwo)
If you hide the label then you will not able to touch again that label as it is hidden now.
To hide a label you can you this trick.
When you tap on label then you can check that...
var tempText = "" //temperory property to store value or label
#objc func tapDetected(_ sender: UITapGestureRecognizer) {
if let text = label.text, !text.isEmpty {
tempText = lbl.text.text
label.text = " "
} else {
label.text = tempText
}
}
You only need tapGesture
let tab = UITapGestureRecognizer(target: self, action: #selector(availabelBalance(_:)))
tab.numberOfTapsRequired = 1
tab.cancelsTouchesInView = false
accountBalanceView.addGestureRecognizer(tab)
//
#objc func availabelBalance(_ sender:UITapGestureRecognizer) {
if lbl.text == "" {
lbl.text = value
}
else {
lbl.text = ""
}
}
Here is sample code
import UIKit
class firstViewController: UIViewController {
#IBOutlet var textLbl: UILabel!
var tab : UITapGestureRecognizer?
override func viewDidLoad() {
super.viewDidLoad()
tab = UITapGestureRecognizer(target: self, action: #selector(availabelBalance))
tab?.numberOfTapsRequired = 1
tab?.cancelsTouchesInView = false
self.view.addGestureRecognizer(tab!)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#objc func availabelBalance(_ sender:UITapGestureRecognizer) {
if (tab?.cancelsTouchesInView)! {
textLbl.isHidden = true
tab?.cancelsTouchesInView = false
}else{
textLbl.isHidden = false
tab?.cancelsTouchesInView = true
}
}
}

How can we add a UIGestureRecognizer to Outlet Collection?

I'm trying to add a tap gesture to an outlet collection of labels [UILabel], like this:
#IBOutlet var subLabels: [UILabel]!
override func viewDidLoad() {
super.viewDidLoad()
let tap = UITapGestureRecognizer(target: self, action: #selector(HomePageViewController.selectSubLabel(tap:)))
tap.numberOfTapsRequired = 1
tap.cancelsTouchesInView = false
for i in (0..<(subLabels.count)) {
subLabels[i].addGestureRecognizer(tap)
}
}
func selectSubLabel(tap: UITapGestureRecognizer) {
print("Gesture Is WORKING!")
}
and i tried to add it on a single label in storyboard; but NONE are working.
Firstly, you need to allow user interaction on a label (it is turned off by default):
for i in (0..<(subLabels.count)) {
subLabels[i].isUserInteractionEnabled = true
subLabels[i].addGestureRecognizer(tap)
}
but gesture recognizer can observe for gestures only in one view.
So, there are two options:
I. Dedicated gesture recognizer for every label
for i in (0..<(labels.count)) {
let tap = UITapGestureRecognizer(target: self, action: #selector(selectSubLabel(tap:)))
labels[i].isUserInteractionEnabled = true
labels[i].addGestureRecognizer(tap)
}
II. One gesture recognizer for the parent view of the labels
override func viewDidLoad() {
super.viewDidLoad()
for i in (0..<(labels.count)) {
subLabels[i].isUserInteractionEnabled = true
}
let tap = UITapGestureRecognizer(target: self, action: #selector(selectSubLabel(tap:)))
view.addGestureRecognizer(tap)
}
func selectSubLabel(tap: UITapGestureRecognizer) {
let touchPoint = tap.location(in: view)
guard let label = subLabels.first(where: { $0.frame.contains(touchPoint) }) else { return }
// Do your stuff with the label
}
Please check the User Interaction Enabled Attribute of your UIlabel's in Attribute inspector of Xcode. User Interaction Enabled must be ticked for detecting the tap. Please see the picture below,

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 to get UILabel to respond to tap?

I have discovered that I can create UILabel much faster than UITextField and I plan to use UILabel most of the time for my data display app.
To make a long story short though, I wish to let the user tap on a UILabel and have my callback respond to that. Is that possible?
Thanks.
You can add a UITapGestureRecognizer instance to your UILabel.
For example:
UITapGestureRecognizer *tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(labelTapped)];
tapGestureRecognizer.numberOfTapsRequired = 1;
[myLabel addGestureRecognizer:tapGestureRecognizer];
myLabel.userInteractionEnabled = YES;
If you're using storyboards you can do this entire process in the storyboard with no additional code. Add a label to the storyboard, then add a tap gesture to the label. In the Utilities pane, make sure "User Interaction Enabled" is checked for the label. From the tap gesture (at the bottom of your view controller in the storyboard), ctrl+click and drag to your ViewController.h file and create an Action. Then implement the action in the ViewController.m file.
Swift 3.0
Initialize the gesture for tempLabel
tempLabel?.text = "Label"
let tapAction = UITapGestureRecognizer(target: self, action: #selector(self.actionTapped(_:)))
tempLabel?.isUserInteractionEnabled = true
tempLabel?.addGestureRecognizer(tapAction)
Action receiver
func actionTapped(_ sender: UITapGestureRecognizer) {
// code here
}
Swift 4.0
Initialize the gesture for tempLabel
tempLabel?.text = "Label"
let tapAction = UITapGestureRecognizer(target: self, action:#selector(actionTapped(_:)))
tempLabel?.isUserInteractionEnabled = true
tempLabel?.addGestureRecognizer(tapAction)
Action receiver
func actionTapped(_ sender: UITapGestureRecognizer) {
// code here
}
Swift 2.0:
I am adding a nsmutable string as sampleLabel's text, enabling user interaction, adding a tap gesture and trigger a method.
override func viewDidLoad() {
super.viewDidLoad()
let newsString: NSMutableAttributedString = NSMutableAttributedString(string: "Tap here to read the latest Football News.")
newsString.addAttributes([NSUnderlineStyleAttributeName: NSUnderlineStyle.StyleDouble.rawValue], range: NSMakeRange(4, 4))
sampleLabel.attributedText = newsString.copy() as? NSAttributedString
let tapGesture: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: "tapResponse:")
tapGesture.numberOfTapsRequired = 1
sampleLabel.userInteractionEnabled = true
sampleLabel.addGestureRecognizer(tapGesture)
}
func tapResponse(recognizer: UITapGestureRecognizer) {
print("tap")
}
You could use a UIButton instead and set the text to what you want. The button doesn't have to look like a button if you don't want to
To add Tap gesture on UILable
UITapGestureRecognizer *tapAction = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(lblClick:)];
tapAction.delegate =self;
tapAction.numberOfTapsRequired = 1;
//Enable the lable UserIntraction
lblAction.userInteractionEnabled = YES;
[lblAction addGestureRecognizer:tapAction];
and to assess the selector method
- (void)lblClick:(UITapGestureRecognizer *)tapGesture {
}
Note: Add UIGestureRecognizerDelegate in .h file
Swift Version:
var tapGesture : UITapGestureRecognizer = UITapGestureRecognizer()
Then inside viewDidLoad(),add this:
let yourLbl=UILabel(frame: CGRectMake(x,y,width,height)) as UILabel!
yourLbl.text = "SignUp"
tapGesture.numberOfTapsRequired = 1
yourLbl.addGestureRecognizer(tapGesture)
yourLbl.userInteractionEnabled = true
tapGesture.addTarget(self, action: "yourLblTapped:")
This works great in Xcode 12 and Swift 5
let tapAction = UITapGestureRecognizer(target: self,action:#selector(actionTapped(_:)))
userLabel?.isUserInteractionEnabled = true
userLabel?.addGestureRecognizer(tapAction)
And action method like -
#objc func actionTapped(_ sender: UITapGestureRecognizer) {
print("tapped")
}
If you want to use Multi line text in your button then create a UILabel with Multiline text and add as a subview in to your button.
for eg:
yourLabel=[Uilabel alloc]init];
yourLabel.frame=yourButtom.Frame;//(frame size should be equal to your button's frame)
[yourButton addSubView:yourLabel]
Swift 3 from Alvin George
override func viewDidLoad() {
super.viewDidLoad()
let newsString: NSMutableAttributedString = NSMutableAttributedString(string: "Tap here to read the latest Football News.")
newsString.addAttributes([NSUnderlineStyleAttributeName: NSUnderlineStyle.styleDouble.rawValue], range: NSMakeRange(4, 4))
sampleLabel.attributedText = newsString.copy() as? NSAttributedString
let tapGesture: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(ViewController.tapResponse))
tapGesture.numberOfTapsRequired = 1
sampleLabel.isUserInteractionEnabled = true
sampleLabel.addGestureRecognizer(tapGesture)
}
func tapResponse(recognizer: UITapGestureRecognizer) {
print("tap")
}
Swift version looks like this:
func addGestureRecognizerLabel(){
//Create a instance, in this case I used UITapGestureRecognizer,
//in the docs you can see all kinds of gestures
let gestureRecognizer = UITapGestureRecognizer()
//Gesture configuration
gestureRecognizer.numberOfTapsRequired = 1
gestureRecognizer.numberOfTouchesRequired = 1
/*Add the target (You can use UITapGestureRecognizer's init() for this)
This method receives two arguments, a target(in this case is my ViewController)
and the callback, or function that you want to invoke when the user tap it view)*/
gestureRecognizer.addTarget(self, action: "showDatePicker")
//Add this gesture to your view, and "turn on" user interaction
dateLabel.addGestureRecognizer(gestureRecognizer)
dateLabel.userInteractionEnabled = true
}
//How you can see, this function is my "callback"
func showDatePicker(){
//Your code here
print("Hi, was clicked")
}
//To end just invoke to addGestureRecognizerLabel() when
//your viewDidLoad() method is called
override func viewDidLoad() {
super.viewDidLoad()
addGestureRecognizerLabel()
}
I personally prefer the method of writing an extension for UILabel. This is what I use.
import UIKit
extension UILabel {
/**
* A map of actions, mapped as [ instanceIdentifier : action ].
*/
private static var _tapHandlers = [String:(()->Void)]()
/**
* Retrieve the address for this UILabel as a String.
*/
private func getAddressAsString() -> String {
let addr = Unmanaged.passUnretained(self).toOpaque()
return "\(addr)"
}
/**
* Set the on tapped event for the label
*/
func setOnTapped(_ handler: #escaping (()->Void)) {
UILabel._tapHandlers[getAddressAsString()] = handler
let gr = UITapGestureRecognizer(target: self, action: #selector(onTapped))
gr.numberOfTapsRequired = 1
self.addGestureRecognizer(gr)
self.isUserInteractionEnabled = true
}
/**
* Handle the tap event.
*/
#objc private func onTapped() {
UILabel._tapHandlers[self.getAddressAsString()]?()
}
}
You would then use it like this from any UILabel instance:
myLabel.setOnTapped {
// do something
}
This can potentially cause some memory leaks I believe, but I haven't determined how best to resolve them yet.

Resources