UITextView can't dismiss the keyboard in programmatically-created UITableView - ios

There seems to be lots on this subject. But I couldn't get any of the solutions to work.
My code creates a tableView full of cells with varying contents (based on a JSON), the user should enter the info in each cell. The problem I am having is, when the user taps somewhere outside the cell (i.e. on the tableView background) I want the keyboard to dismiss.
didSelectRowAtIndexPath: method is not good.
touchesBegan: does not fire at all (tableView has user interaction enabled, but I assume there is some other reason).
I have added a gesture to the tableView, but then I cannot start editing.
I know the resignFirstResponder. But I don't know which field is being edited. so I think, I need to go with the endEditing: method. But I just couldn't get it called, when user touches outside of a cell.
Any help would appreciated.

If that is a textField as #JFS said, you can set the delegate and resign it in this below method.
- (BOOL)textFieldShouldReturn:(UITextField *)textField
Else If that is a textView, You can add a toolbar with done button as inputAccessoryView to your textView and resign your textView.
I am having two more ideas
Bad Idea:
Try to make an invisible button in the cell background and add an action method to resign it. But this will get confused, when you are using didSelectRowAtIndexPath: method of your UITableView.
Good Idea:(Good one)
UITableView surely will have UIScrollview. So in the scrollViewDidScroll: method, set [self endEditing:YES] will surely work.
Good Idea 2
Create a new View or Button and place it on top of the all views, when a textView is in editing and call the endEditing: method when user touches the view or button.
TO GET A TOUCH DETECTION IN TABLEVIEW:
Some of these Q&A will help you.
https://stackoverflow.com/a/8787019/1083859
Why does my UITableView not respond to touchesBegan?
https://stackoverflow.com/a/8786706/1083859

Okay after lots of playing around a managed to figure (something) out, it is not great but works.
Most of the gaps around the table is taken up by section headers (a small amount by the cell walls). To get these areas to call a method I added a gesture to the section header like this;
-(UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
NSString *sectionTitle = [self tableView:tableView titleForHeaderInSection:section];
if (sectionTitle == nil)
{
return nil;
}
UILabel *label = [[UILabel alloc] init];
label.frame = CGRectMake(20, 8, 320, 20);
label.backgroundColor = [UIColor clearColor];
label.textColor = [UIColor colorWithRed:76/255.0 green:86/255.0 blue:108/255.0 alpha:255/255.0];
label.shadowColor = [UIColor grayColor];
label.shadowOffset = CGSizeMake(-0.0, 0.0);
label.font = [UIFont boldSystemFontOfSize:16];
label.text = sectionTitle;
UIView *view = [[UIView alloc] init];
[view addSubview:label];
UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc]initWithTarget:self action:#selector(eventMenuTap)];
[view addGestureRecognizer:singleTap];
return view;
}
This also enabled me to customize the label for each section. The net reseult is now I have the keyboard closing when the table is scrolled and if (nearly) anywhere is clicked outside of a cell (i.e. a header) then the eventMenuTap is called.
-(void)eventMenuTap
{
NSLog(#"Tap has begun...");
[self.view endEditing:YES];
}
Thanks for all the ideas and help with this.

For any passersby, the simplest and most effective solution I have found requires 2 things:
1: Subclass your table view and add the following to its .m:
- (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
// send your own dismiss message; you can use self.delegate to refer to TV's TVC...
[((SearchVC *)((UITableViewController *)self.delegate).parentViewController).searchBar resignFirstResponder];
// crucial!
[super touchesBegan:touches withEvent:event];
}
2: Add the following to your TVC:
- (void) scrollViewWillBeginDragging:(UIScrollView *)scrollView
{
// send your own dismiss message...
[((SearchVC *)self.parentViewController).searchBar resignFirstResponder];
}
This works beautifully for me. I initially tried adding touchesBegan to my subclassed cell, but found that was insufficient and, after adding these two things, superfluous.

Related

UIButton not working in UITableview

Have UITableviewcell --> inside which i have 4 different UIView and 3 of the views has UIButtons. I want to make UIButton clickable. For the first time the buttons are clickable but when i go to next screen and come back, the buttons don't work. Please let me know how to implement this? Thanks!
PHMyTripView *tripsView=[[PHMyTripView alloc] initWithFrame:CGRectMake(10, 5, self.frame.size.width-20, cell.frame.size.height)];
tripsView.onBaggageClick = ^{
[weakSelf handleBaggagePurchaseTapped];
};
if ([data count]>0) {
[tripsView fillTripData:[data firstObject]];
[tripsView showAncillaries:self.predictiveManager.upcomingCDAirBookingInfo];
}
[cell bringSubviewToFront:tripsView.bagsButton];
[cell.viewPlaceHolder addSubview:tripsView];
This happens because the cells are reusable and if you go to another screen the button may kinda mess round into different cell in UI or functionally.
The way to solve this is to add tag. You can define cell.tag = indexPath.row * 10 or so.
Then when draw the button, you check the condition, if cell.tag == ?, then add your button.
Thank you everyone for your response but they din't work in my case.
Here is the answer. Since the UITableview was reloading when it comes back to the screen.The frames were mis placed and Hence the button was not able to click.
Used this one line code which worked fine.
self.tripsView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
You should turn off delaysContentTouches on each UITableView subview, which is an instance of UIScrollView class.
Subclass UITableView and override initWithFrame method:
.h file
#interface NoDelaysOnTouchTableView : UITableView
#end
And .m file
#import "NoDelaysOnTouchTableView.h"
#implementation NoDelaysOnTouchTableView
- (id) initWithFrame: (CGRect) frame
{
self = [super initWithFrame: frame];
if (self) {
for (id view in self.subviews) {
if ([NSStringFromClass([view class]) isEqualToString: #"UITableViewWrapperView"]) {
if ([view isKindOfClass: [UIScrollView class]]) {
// turn OFF delaysContentTouches in the hidden subview
UIScrollView* scroll = (UIScrollView*)view;
scroll.delaysContentTouches = NO;
}
break;
}
}
}
return self;
}
#end
Next - use this subclass (create an instance of NoDelaysOnTouchTableView) to be able to press UIButton immediately.
I think that the causes would be multiples.
You can try:
check the constraints for each button in your cell
in cellForRow you can do addTarget: for each button with relative #selector and in this method check the object (datasource) associated to button.
do not use addSubView in your cell, instead use a Xib (or define your cell in a storyboard within tableView) so you can set the constraints.
I Hope, i've helped you.
Not specifically about this issue, but for some people it may be the case.Try to add subviews in contentView of the cell.
Instead of addSubview(textField) use contentView.addSubview(textField)

Detecting touches on an underlying UITableViewCell and taps in attributed text of a UITextview

I use a UITextView that is attached to a UITableViewCell with a configuration as shown below:
cell.topicAndDescriptionTextView = [[UITextView alloc] initWithFrame:frame];
cell.topicAndDescriptionTextView.tag = 1;
cell.topicAndDescriptionTextView.attributedText = attrText;
cell.topicAndDescriptionTextView.scrollEnabled = NO;
cell.topicAndDescriptionTextView.editable = NO;
cell.topicAndDescriptionTextView.textContainer.lineFragmentPadding = 0;
cell.topicAndDescriptionTextView.textContainerInset = UIEdgeInsetsMake(0, 0, 0, 0);
cell.topicAndDescriptionTextView.backgroundColor =[UIColor clearColor];
[cell.topicAndDescriptionTextView setUserInteractionEnabled:YES];
cell.topicAndDescriptionTextView.dataDetectorTypes = UIDataDetectorTypeAll;
Now I would like to use text kit feature to detect taps on custom links in my attributed text (http://www.raywenderlich.com/48001/easily-overlooked-new-features-ios-7#textViewLinks) and - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath to identify a drilldown if a customer clicks somewhere in the UITableViewCell where there is no custom link.
Unfortunately [UITextView setUserInteractionEnabled:YES] makes the textview swallow all touches. What is the best approach to achieve this? I thought of writing a custom UITextView Class, and use UITextView delegates, but how can I then identify if the super class handles the press on a link already and to block a drilldown?
THX,
Jan
I would try to override hitTest and only return a hit view if the returned view is the one that is used for link buttons. In other cases return nil. Then the touch is passed to the table view instead and the scrolling keeps working.
I found the solution. I created my custom UITextView Class and override touchesBegan. Touches Began is only called if no link is clicked in UITextView. I played around with hitTest, but hitTest is called all the time and I can't determine if a link was touched or not inside the UITextView.

Superview isn't getting touch events

I have a simple iPad app with the following layers of views:
BaseView (covers the entire available area).
SquareView (covers 100x100px area).
The SquareView is added as subview to BaseView.
I also have a BaseViewController.m file. In this file I added the touchesBegan and touchesEnded functions to handle touch events. The problem is, when I click on the SquareView the touches functions get called, but when I click anywhere else they don't get called.
I have userInteractionEnabled = YES for the SquareView. I need this variable to be set in order to receive touches.
Clicking on the area outside the SquareView does not trigger the touches functions. I am puzzled as to why. Setting the userInteractionEnabled variable in the BaseView has no impact. What could be happening here? Is there some rule that I am missing? I am pretty sure I had it working at one point.
When the user clicks on the subview (SquareView) I need to launch a menu (let's say MenuView class). The idea is, when the user clicks on the area outside of the menu (or SquareView) I need to remove the pop-up MenuView by calling [menuView removeFromSuperview]. Is there a better way to handle this?
You could try to use this code in your controller. It should work for you.
- (void)viewDidLoad
{
[super viewDidLoad];
UIView *subview = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 100, 100)];
subview.backgroundColor = [UIColor lightGrayColor];
[subview addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self
action:#selector(subviewTouched)]];
[self.view addSubview:subview];
[self.view addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self
action:#selector(viewTouched)]];
}
- (void)viewTouched
{
NSLog(#"viewTouched");
}
- (void)subviewTouched
{
NSLog(#"subviewTouched");
}

UIView on top of a UITableView when cell is clicked?

What I am trying to do is display a smaller view on top of a UITableView when a UITableViewCell is clicked. I don't want to transition to another UIViewController but "popup" a view on top of the UITableView that will house more information about the UITableViewCell clicked.
I don't think I am looking for a UIAlertView, but I am looking for a UIView that I can put labels, buttons, pictures, etc. on.
I hope my terminology is correct. I am still a newb :)
Leon
p.s. All my searching just came up with UIAlertView stuff.
Try using didSelectRowAtIndexPath delegate function of the UITableView, the code is untested to give you an idea:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
// Assuming view is at zero index of XIB file.
// this view will contain all lable and other controls
UIView *customView = (UIView *)[[NSBundle mainBundle] loadNibNamed:#"NameOfCustomViewXIBFile" owner:nil options:nil] objectAtIndex:0];
customView.transform = CGAffineTransformMakeScale(0.0f, 0.0f);
[self.view addSubView:customView];
[UIView animateWithDuration:0.5
animations:
^{
customView.transform = CGAffineTransformMakeScale(1.0f, 1.0f);
}
];
}
Hope it helps!
EDIT:
Animation to remove this popup:
customView.transform = CGAffineTransformMakeScale(1.0f, 1.0f);
[UIView animateWithDuration:0.5
animations:
^{
customView.transform = CGAffineTransformMakeScale(0.0f, 0.0f);
}
];
[customView removeFromSuperView];
The problem with the existing answer is that if the user scrolls the table so that the cell goes offscreen and back on, the view that you popped up is likely to be gone due to cell re-use. To avoid this, you must store something in the cell's "model" so that every time cellForRowAtIndexPath: is called, you re-generate the cell with its popup view if it is supposed to be there. Just displaying from didSelectRowAtIndexPath: may be insufficient.
The approach I'd prefer for this if I were coding it is to make all the cells your own subclass of UITableViewCell. This subclass includes an extra BOOL showPopup. In your setter setShowPopup: you set the value, and also create & show or destroy/remove/hide the "popup" subview for that cell. (You could make the subview a class member that's always around, allocate and assign it when needed, and keep a reference, just showing/hiding as needed; that's a size/space performance trade-off.)
-(void) setShowPopup:(BOOL show) {
showPopup = show;
if(show) {
// create subview, and add to view
} else {
// remove and destroy subview
}
}
And in UITableView delegate/datasource:
-(UITableViewCell) UITableView:(UITableView *table) cellForRowAtIndexPath:(NSIndexPath *ip) {
// usual stuff to get a reusable cell or allocate a new one, and prepare it for display
// then
if(showPopup) {
// create subview and add to view
} else {
// remove and destroy subview
}
}
I used a UIButton for something similar in a GLKViewController so this might work for a UITableViewController. I just disabled the button and added a rounded border so it looked like a custom popup. When the user clicked on something interesting, I changed the hidden flag on the button to show or hide it.
Something like this to make it look pop-up-y
In viewDidLoad:
// make info "popup"
_infoBtn = [UIButton buttonWithType:UIButtonTypeCustom];
_infoBtn.bounds = CGRectMake( 0, 0, kRightButtonBarDim, kRightButtonBarDim);
_infoBtn.backgroundColor = [UIColor colorWithWhite:1 alpha:0.75];
_infoBtn.selected = NO;
_infoBtn.titleLabel.font = [UIFont boldSystemFontOfSize:kTableCellFontSize];
_infoBtn.titleLabel.lineBreakMode = NSLineBreakByCharWrapping;
[_infoBtn setTitleColor:[UIColor colorWithWhite:.3 alpha:1] forState:UIControlStateDisabled];
// border
_infoBtn.layer.cornerRadius = 5; // rounded corners
// drop shadow
_infoBtn.layer.masksToBounds = NO;
_infoBtn.layer.shadowColor = [UIColor blackColor].CGColor;
_infoBtn.layer.shadowOpacity = 0.8;
_infoBtn.layer.shadowRadius = 12;
_infoBtn.layer.shadowOffset = CGSizeMake(12.0f, 12.0f);
_infoBtn.enabled = NO;
_infoBtn.hidden = YES;
[self.view addSubview:_infoBtn];
Then in tableView's didSelectRowAtIndexPath update any information needed in the button and set hidden to NO. You should be able to add any necessary subviews to this UIButton.
_infoBtn.frame = CGRectMake(20, 20, 20, 20); // set this wherever you like
[_infoBtn setTitle:#"text" forState:UIControlStateDisabled];
_infoBtn.hidden = NO;
I just tested this for a UIViewController that has a UITableView as a subview and it looked the same. Scrolling did not affect it (i.e., the button did not move with scrolling, almost like a HUD) because the button was added to the top-level UIView. Dunno if this is what you need, but hopefully it will give you some ideas.

iOS: Removing UIView that's on top of UITableView leads to inability to interact with table

I have a UITableView with a bunch of rows. When a user taps on a row, a custom pop-up (which is a custom UIView) will appear on top of the table:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
PopUp *myPopUp = [[PopUp alloc] initWithFrame:CGRectMake(0, 0, 320, 568)];
[self.view addSubview:myPopUp];
}
I'm loading my custom UIView PopUp from a nib:
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self loadNib];
}
return self;
}
- (void) loadNib
{
NSArray *subviewArray = [[NSBundle mainBundle] loadNibNamed:#"PopUp" owner:self options:nil];
UIView *mainView = [subviewArray objectAtIndex:0];
[self addSubview:mainView];
}
In the PopUp, there is a button when pressed that causes the PopUp to close:
- (IBAction)closePopUp:(id)sender
{
[self removeFromSuperview];
}
The PopUp disappears when the button is pressed. However, the UITableView underneath cannot be interacted with anymore (i.e. the user cannot scroll the table, cannot tap on another row, etc.). I would like the PopUp to disappear and have the table be fully interactive again. Can anyone explain why this is happening and how I may fix this? Thanks!
Edited with screenshots
UITableView with a row of data: http://imgur.com/PlIufHI,xGKxUul,qkt27oZ#0
When a row is selected, myPopUp appears on top: http://imgur.com/PlIufHI,xGKxUul,qkt27oZ#1
When the "x" custom button is pressed, it calls closePopUp, which removes myPopUp from the superview: http://imgur.com/PlIufHI,xGKxUul,qkt27oZ#2
User is unable to interact with the table now. User cannot select a row, scroll through the table, etc.
You are actually removing the view that you loaded from the nib file, but the parent is another blank UIView that is capturing every touch within the (0, 0, 320, 568) rect.
Try removing the superview from the closePopUp method:
[self.superview removeFromSuperview];
I don't know what's going on in your specific case, but I can tell you that adding subviews to a UITableView may lead to unexpected behavior like this and it's generally a bad idea.
In order to fix what's happening and get a cleaner structure, I would suggest you to add the popup view to the window, rather than to the table view.
[self.view.window addSubview:myPopUp];

Resources