I have a custom UICollectionReusableView that is the header view of a UICollectionView. There is a label on this header view, and I'd like to handle a tap event on the label.
For some reason, I'm unable to CTRL drag to create an IBAction to handle the click event. I've also tried adding a UITapGestureRecognizer to the label in the view's initWithCoder(). Although initWithCoder() gets called, the callback never gets called when the label is tapped.
Your help would be most appreciated! Thank you!
Here's the code inside the UICollectionReusableView:
-(id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
self.tapRecogniser = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(onLabelTap:)];
[self.label1 addGestureRecognizer:self.tapRecogniser];
return self;
}
-(void)onLabelTap:(UITapGestureRecognizer *)tapGestureRecognizer;
{
NSLog(#"Label tapped");
}
The default value of userInteractionEnabled property is FALSE for the class UILabel. Try to set it to TRUE.
Related
I have a UITextField that is part of a UITableViewCell, the table should be a dumb component.
The events triggered by the UITextField are handled by a custom delegate, which:
Every value change (typing, spinning the picker,...) triggers a chain of events to store and process the data
At the end of the chain UITableView is fully reloaded.
The full reload causes the inputView to disappear.
While I want 1. and 2., I don't want 3. The inputView should remain open and focused on the UITextField being edited -> I am looking for a way to open/close it programmatically and perhaps place it in -(void)prepareForReuse of the UITableViewCell
My most generic custom UITableViewCell initialization:
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
self.inputField = [UITextField new];
[self.inputField addTarget:self
action:#selector(textFieldDidChange:)
forControlEvents:UIControlEventEditingChanged];
[self.contentView addSubview:self.inputField];
[self.inputField setTranslatesAutoresizingMaskIntoConstraints:NO];
[self.inputField.leftAnchor constraintEqualToAnchor:self.contentView.leftAnchor].active = YES;
[self.inputField.rightAnchor constraintEqualToAnchor:self.contentView.rightAnchor].active = YES;
[self.inputField.topAnchor constraintEqualToAnchor:self.contentView.topAnchor].active = YES;
[self.inputField.bottomAnchor constraintEqualToAnchor:self.contentView.bottomAnchor].active = YES;
}
return self;
}
Example of change handling (inputView could be a standard keyboard, UIPickerView, or something custom):
-(void) textFieldDidChange:(UITextField *)textField {
NSString *valueString = textField.text;
[self.delegate handleValueChange:valueString forAttributeID:self.attributeID];
}
I think the problem is that once you call reloadData, new cells will be created for all visible sections/rows, so the original input view loses its first responder state.
There are many ways you could circumvent this. Maybe the most easy one is to not call reloadData, but instead just reload the cells that are "changed" (´reloadRowsAtIndexPaths:withRowAnimation:`) - and especially not to reload the cell with the inputView that caused the change
Or, you could store the "active" IndexPath, and after refresh let the appropriate inputView become first responder again.
There might be other solutions to think of - all of them depend on your workflow.
I want to add a gesture recognizer to a UIImageView in a UITableViewCell.
I've subclassed this cell, and implemented the gesture code, but it does not seem to be working .
This is the code for my subclassed .m file:
- (id)initWithCoder:(NSCoder *)coder {
self = [super initWithCoder:coder];
if (self) {
[self.settingsView setUserInteractionEnabled:YES];
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(didRecognizeTapOnSettings)];
[tap setNumberOfTouchesRequired:1];
[tap setNumberOfTapsRequired:1];
[self.settingsView addGestureRecognizer:tap];
}
return self;
}
I'm using initwithcoder since I'm using storyboards, and that seems to be working far better than initwithframe. For some odd reason, didRecognizeTapOnSettings does not get called. I've set the delegate of the UIGestureRecognizer in my .h file as well.
IBOutlets are not yet set inside an init method, so self.settingsView is going to be nil. Move the code you have in the if(self) block to awakeFromNib, and then it should work.
try using
[tap setMinimumPressDuration:0.1f];
then you said you are setting your delegate for the gesture then if you are planning to use your delegate methods for the tapGesture you should set the delegate to self.
tap.delegate = self;
What's the hierarchy in your views? Your UITableViewCell might be catching the tap event instead of your UIImageView. As an experiment try to disable the userInteractions in the UITableViewCell contentView I'm assuming that you added the UIImageView in the contentView of your cell.
Let me know if that worked
In Instagram in the header view we could see there is the profile image and the username right , so I am thinking how is this possible because the username is a UILabel how when it's clicked on it sent you the user homepage . Any idea ?
You can add a UITapGestureRecognizer to the label to make it clickable.
UITapGestureRecognizer* gesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tappedOnLabel:)];
[label setUserInteractionEnabled:YES];
[label addGestureRecognizer:gesture];
-(void)tappedOnLabel:(UIGestureRecognizer*)gestureRecognizer
{
// Perform your action
}
Yes it can, check "User Interaction Enabled" in the Attribute Inspector, and add an Action method with it. Make sure you connect the action method to the label
EDIT-
I am showing this in code as i cant post screenshots.
Create a Label #property(nonatomic, weak) IBOutlet UILabel *myLabel;
Connect the Label in Storyboard.
Create a method, that you want to do when user Taps the label.
-(void)showHello{
NSLog(#"Hello World");
}
Now declare a
UITapGestureRecognizer *tap;
I used tapGesture as I want the action to run when I TAP the label.Declare this Gesture as an Instance Variable
in viewDidLoad alloc and init the gesture and add it to the Label
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.myLabel.userInteractionEnabled = YES;
tap = [[UITapGestureRecognizer alloc]initWithTarget:self action:#selector(showHello)];
[self.myLabel addGestureRecognizer:tap];
}
Yup that should do it
Next, I learned that to close the virtual keyboard when clicked anywhere on the screen, we must implement the touchesBegan method together with the name of the variables, which bind with the existing view UITextFields.
If we have about 10 text fields, I learned that I have to repeat this command 10 times:
[MyFristTextField resignFristResponder];
[MySecondTextField resignFristResponder];
...
[MyTenTextField resignFristResponder];
Is not there any easier way to do this, for example, call UITextFields all at once?
Keep a reference to the current UITextField in a property. Then send resignFirstResponder to that UITextField when you want the keyboard to dismiss.
You do not need to implement 10 times.
What you can do is just use:
[self.view endEditing:YES];
At the method that you are calling from the tap Gesturrecgnizer.
For example:
self.tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(closeAllKeyBourds)];
[self.view addGestureRecognizer:self.tap];
Your code should look something like:
-(void)textFieldDidBeginEditing:(UITextField *)textField
{
self.tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(closeAllKeyBourds)];
[self.view addGestureRecognizer:self.tap];
}
{
[self.view endEditing:YES];
[self.view removeGestureRecognizer:self.tap];
self.tap = nil;
}
Tip:
If you are using 10 TextField, you better place then on a scroll view, so you can lift the scroll view up when using a lower text Field.
Hope this helps!
Edit
You have to keep an instance variable, or a property of the TapGestureRecognizer, so you can refer to it when the tap occurred.
So you keep a property at your #inerface, it should look like so:
//At your interface:
#interface LogInpageViewController : UIViewController
//Keep a property
#property (nonatomic,strong) UITapGestureRecognizer *tap;
I have a UITableView with an associated UITableViewController. However, I've modified the table to also have a view with a textfield subview.
As always, I want the keyboard to disappear when the user hits 'done' (easy) and when they touch anywhere else on screen other than the textfield (hard, stuck!).
The normal way to achieve this is to change the class to UIControl so it can handle actions... but I can't do this for my UITableView/UITableViewController combination.
How can I solve this problem?
U can handle user touches by adding a UITapGestureRecognizer to your view.
For example if u don't want to enable row selection in your tableView u call self.tableView.allowsSelection = NO;
But if u still want to detect user touches u add a UITapGestureRecognizer to your tableView (or to tableView.superview).
U can have more control if u implement the UIGestureRecognizerDelegate, this way u can detect and then choose witch touches to receive and witch not.
To do that just add this code to your UITableViewController:
- (void)viewDidLoad
{
[super viewDidLoad];
self.tableView.allowsSelection = NO;
UITapGestureRecognizer *tgr = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(viewTapped:)];
tgr.delegate = self;
[self.tableView addGestureRecognizer:tgr]; // or [self.view addGestureRecognizer:tgr];
[tgr release];
}
- (void)viewTapped:(UITapGestureRecognizer *)tgr
{
NSLog(#"view tapped");
// remove keyboard
}
// this is optional, it let u choose witch touches to receive, for example here I'm checking if user has tapped on a textField
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
if ([touch.view isKindOfClass:[UITextField class]]) {
NSLog(#"User tapped on UITextField");
}
return YES; // do whatever u want here
}
A normal practice is to put a custom UIButton( it becomes visible only when uitextfield begins editing ) behind keyboard view, and when user clicks on screen he actually clicks on that button, and associated selector can resign first responder.
-(void) closeKeyboard:(UIButton *) b {
[self.view endEditing:YES]; //assuming self is your top view controller.
[b setHidden:YES];
}
Using endEditing is better, cause it loops through all subviews and looks for current first responder.
Using alloc breaks with ARC enabled
Just add the following to the viewController
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
//where text field is a #property (nonatomic,retain) IBOutlet to your textfield
[textField resignFirstResponder];
}
When a row is tapped the didSelectRowAtIndexPath is called. If a textField located inside inside a row is tapped then the textfield delegate is called.
So in addition to your done button method, in didSelectRowAtIndexPath add a check for the text field being first responder and ask it to resign. Assuming a the selected indexPath is not the row of the textfield.