UIButton not working in UITableview - ios

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)

Related

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.

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

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.

How can I keep an UIimageView on top of other uiimageviews when using pan or other gestures

I have a ViewController on which I can move multiple UIImageViews using gesture recognizers. it's working fine but the UIImageView selected is not on top of the others.
The UIImageViews keep the order of their creation so the last ones are always bellow the first ones.
Any advice?
When you've selected your new UIImageView - from your UIViewController you should call:
[self.view bringSubviewToFront:imageView];
That should help.
You can use,
[superView bringSubviewToFront:touchedview]; for bringing the subview to the front
What you need to do it programmatically reorder them . You can do this in the following way
-(void)bringTheDesiredImageViewInFront:(int))_tagForImageView
{
for(UIImageView *_subView in self.view.subviews)
{
if([_subView isKindOfClass:[UIImageView class]])
{
UIImageView *temp=(UIImageView *)_subView;
if([temp tag]==_tagForImageView)
[self.view bringSubviewToFront:temp];
}
else
continue;
}
}
Call this method whenever the selector for any gestureRecognizer is called on any UIImageView . Also set the different tag values for different UIImageView . The above method could be called like this ,
UIImageView *imageView=[[UIImageView alloc] init];
[imageView setTag:1];
[self bringTheDesiredImageViewInFront:imageView.tag]

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];

iOS: setting Exclusive Touch to all buttons in a view

My app has many buttons in a Window and I want to set Exclusive Touch all of them together. Do you have any suggestion about this? Thanks
There is a way to set exclusive touch to all buttons in your app, may be helpful.
#import </usr/include/objc/objc-class.h>
static IMP gOringinalWillMoveToSuperview = nil;
static id newMoveToSuperviewPlusSettingExclusiveTouch(id self,SEL selector,...)
{
va_list arg_list;
va_start( arg_list,selector);
gOringinalWillMoveToSuperview(self,selector,arg_list);
[self setExclusiveTouch:YES];
return nil;
}
-(void)addSettingExclusiveTouchToAllUIViewMethodWillMoveToSuperview
{
gOringinalWillMoveToSuperview = class_getMethodImplementation([UIButton class], #selector(willMoveToSuperview:));
class_replaceMethod([UIButton class], #selector(willMoveToSuperview:), &newMoveToSuperviewPlusSettingExclusiveTouch, "v#:");
}
if you don't understand this, you can refer to this and this.
Are you just looking for an easy way to set them all at once?
If you have all the buttons in an array (e.g. they're all connected to the same IBOutletCollection) you can use key value coding to set the exclusiveTouch property of the array:
[buttonArray setValue:[NSNumber numberWithBool:YES] forKey:#"exclusiveTouch"];
NSArray will then invoke the same method on every item in the array.
-(void)setExclusiveTouchForButtons:(UIView *)myView
{
for (UIView * v in [myView subviews]) {
if([v isKindOfClass:[UIButton class]])
[((UIButton *)v) setExclusiveTouch:YES];
else if ([v isKindOfClass:[UIView class]]){
[self setExclusiveTouchForButtons:v];
}
}
}
then call this function at viewDidAppear
If these buttons are all in the same view, you can loop through the view's subviews, test for whether the particular subview is a button (or test for a tag if you have one set) and set exclusiveTouch on each.
I just found an answer for this:
#pragma mark Set Buttons Exclusive Touch Yes
-(void)setExclusiveTouchForButtons:(UIView *)myView
{
for (UIView * button in [myView subviews]) {
if([button isKindOfClass:[UIButton class]])
[((UIButton *)button) setExclusiveTouch:YES];
}
}
Source
If you want to set exclusiveTouch for ALL UIButtons in your whole application method swizzling will be the perfect solution for you.
This answer explains the way very well : https://stackoverflow.com/a/24534814/976246 , and it works perfectly for me.
Also go through this article to know how this (http://nshipster.com/method-swizzling/) tecknique can be used for various purposes.
If you are adding buttons pragmatically, then send a message to the button [button setExclusiveTouch:YES]; for each buttons before adding to its super view. Else if you are using xib, you have to send the same message to the button in viewDidLoad or in loadView.
Here is some code in swift that will set exclusive touch to all buttons in your viewcontroller's view
for button in self.view.subviews {
if(button.isKindOfClass(UIButton)){
(button as! UIButton).exclusiveTouch = true
}
}
Even though the question was asked many years ago, here is a simple way to set isExclusiveTouch for all subviews that are buttons. This example shows code for subviews of self.view. Change self.view to your view of interest.
Seems like cleaner code for anyone with questions on this in the present.
for subview in self.view.subviews
{
if subview is UIButton {
subview.isExclusiveTouch = true
}
}

Resources