UIButton not showing highlight on tap in iOS7
Solution 1
In that tableview you just add this property.
tableview.delaysContentTouches = NO;
And add in cellForRowAtIndexPath after you initiate the cell you just add below code. The structure of the cell is apparently different in iOS 6 and iOS 7.
iOS 7 we have one control UITableViewCellScrollView In between UITableViewCell and content View.
for (id obj in cell.subviews)
{
if ([NSStringFromClass([obj class]) isEqualToString:@"UITableViewCellScrollView"])
{
UIScrollView *scroll = (UIScrollView *) obj;
scroll.delaysContentTouches = NO;
break;
}
}
Solution 2
Since iOS 8 we need to apply the same technique to UITableView subviews (table contains a hidden UITableViewWrapperView scroll view). There is no need iterate UITableViewCell subviews anymore.
for (UIView *currentView in tableView.subviews) {
if ([currentView isKindOfClass:[UIScrollView class]]) {
((UIScrollView *)currentView).delaysContentTouches = NO;
break;
}
}
This answer should be linked with this question.
Solution 3
I tried to add this to the accepted answer but it never went through. This is a much safer way of turning off the cells delaysContentTouches property as it does not look for a specific class, but rather anything that responds to the selector.
In Cell:
for (id obj in self.subviews) {
if ([obj respondsToSelector:@selector(setDelaysContentTouches:)]) {
[obj setDelaysContentTouches:NO];
}
}
In TableView:
self.tableView.delaysContentTouches = NO;
Solution 4
For a solution that works in both iOS7 and iOS8, create a custom UITableView
subclass and custom UITableViewCell
subclass.
Use this sample UITableView
's initWithFrame:
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self)
{
// iterate over all the UITableView's subviews
for (id view in self.subviews)
{
// looking for a UITableViewWrapperView
if ([NSStringFromClass([view class]) isEqualToString:@"UITableViewWrapperView"])
{
// this test is necessary for safety and because a "UITableViewWrapperView" is NOT a UIScrollView in iOS7
if([view isKindOfClass:[UIScrollView class]])
{
// turn OFF delaysContentTouches in the hidden subview
UIScrollView *scroll = (UIScrollView *) view;
scroll.delaysContentTouches = NO;
}
break;
}
}
}
return self;
}
Use this sample UITableViewCell
's initWithStyle:reuseIdentifier:
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self)
{
// iterate over all the UITableViewCell's subviews
for (id view in self.subviews)
{
// looking for a UITableViewCellScrollView
if ([NSStringFromClass([view class]) isEqualToString:@"UITableViewCellScrollView"])
{
// this test is here for safety only, also there is no UITableViewCellScrollView in iOS8
if([view isKindOfClass:[UIScrollView class]])
{
// turn OFF delaysContentTouches in the hidden subview
UIScrollView *scroll = (UIScrollView *) view;
scroll.delaysContentTouches = NO;
}
break;
}
}
}
return self;
}
Solution 5
What I did to solve the problem was a category of UIButton using the following code :
- (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[super touchesBegan:touches withEvent:event];
[NSOperationQueue.mainQueue addOperationWithBlock:^{ self.highlighted = YES; }];
}
- (void) touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
[super touchesCancelled:touches withEvent:event];
[self performSelector:@selector(setDefault) withObject:nil afterDelay:0.1];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
[super touchesEnded:touches withEvent:event];
[self performSelector:@selector(setDefault) withObject:nil afterDelay:0.1];
}
- (void)setDefault
{
[NSOperationQueue.mainQueue addOperationWithBlock:^{ self.highlighted = NO; }];
}
the button reacts correctly when I press on it in a UITableViewCell, and my UITableView behaves normally as the delaysContentTouches
isn't forced.
Eric
Software Engineer in San Francisco, California. Main focus on iOS development. iOS Engineer at TuneIn Technical Editor / Author at raywenderlich.com Blogging at my personal site Graduated from California Polytechnic State University, San Luis Obispo with a Bachelors in Computer Engineering. I am currently making iOS apps for a living and loving every moment of it!
Updated on July 05, 2022Comments
-
Eric almost 2 years
I've looked at a ton of posts on similar things, but none of them quite match or fix this issue. Since iOS 7, whenever I add a
UIButton
to aUITableViewCell
or even to the footerview it works "fine", meaning it receives the target action, but it doesn't show the little highlight that normally happens as you tap aUIButton
. It makes the UI look funky not showing the button react to touch.I'm pretty sure this counts as a bug in iOS7, but has anyone found a solution or could help me find one :)
Edit: I forgot to mention that it will highlight if I long hold on the button, but not a quick tap like it does if just added to a standard view.
Code:
Creating the button:
UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect]; button.titleLabel.font = [UIFont systemFontOfSize:14]; button.titleLabel.textColor = [UIColor blueColor]; [button setTitle:@"Testing" forState:UIControlStateNormal]; [button addTarget:self action:@selector(buttonPressed:) forControlEvents: UIControlEventTouchDown]; button.frame = CGRectMake(0, 0, self.view.frame.size.width/2, 40);
Things I've Tested:
//Removing gesture recognizers on
UITableView
in case they were getting in the way.for (UIGestureRecognizer *recognizer in self.tableView.gestureRecognizers) { recognizer.enabled = NO; }
//Removing gestures from the Cell
for (UIGestureRecognizer *recognizer in self.contentView.gestureRecognizers) { recognizer.enabled = NO; }
//This shows the little light touch, but this isn't the desired look
button.showsTouchWhenHighlighted = YES;
-
Eric over 10 yearsYou are a genius, thank you! I edited your subview loop to make it a little more future proof. Posted the edit, so once it gets approved will hopefully help others out
-
subramani over 10 yearssure,thanks for posting this. I hope this will be helpful for others also.
-
Fred Collins over 10 yearsThanks man. I tried to write the condition
([obj isKindOfClass:[UITableViewCellScrollView class]])
but it raises an error saying that it doesn't know about a class namedUITableViewCellScrollView
. Why? -
Ryan Romanchuk over 10 yearsBrilliant, been struggling with this one for a while now.
-
Ryan Romanchuk over 10 years@subramani i just noticed in one of my tableviews scrolling has stopped, i need to investigate why, but the only difference is that this cell has multiple buttons. Any ideas what could be happening? @Eric?
-
Ryan Romanchuk over 10 years@subramani actually I think it's related to having multiple scrollviews
-
Eric over 10 years@RyanRomanchuk I've used it with multiple buttons. I know that this does stop scrolling if the start of the scroll (where you tap down) is a button. It's because now the touch gets sent to the button and not the scroll view. I still do this because I think showing the button highlight is more important than making the scroll work even if they start the scroll on the button. That may just be in my case though
-
Ryan Romanchuk over 10 years@Eric strange..my entire scrollview doesn't scroll anymore (regardless of where I'm tapping), but only on one specific controller. The only difference with this view controller is that it has multiple scrollviews.
-
Eric over 10 years@RyanRomanchuk Are you nesting scrollviews, or are there just multiple side by side. If you are nesting, maybe touches aren't being passed to the subscrollViews
-
Ryan Romanchuk over 10 years@Eric there is a scrollview positioned above the uitableview acting as a photo browser/parallax, i haven't investigated much, seems bizarre that it's causing problems, but it's the only variable i can think of at the moment
-
sonxurxo over 10 years@subramani You are my today's favorite person :) Thank you! BTW, sadly SO is full of other wrong answers to the same question...
-
Eric about 10 yearsSo basically setting one cells ts_delaysContentTouches sets the entire tableViews delaysContentTouches correct? So this wouldn't allow dynamic cell setting, but rather setting the whole tableview as a whole?
-
TomSwift about 10 years@Eric - no. I consciously decided not to also have it set the tableview delaysContentTouches. I feel that is a side effect that is unexpected since it would be changing a view upstream in the hierarchy. You still need to set tableView.delaysContentTouches = NO.
-
Marc Etcheverry about 10 yearsIf you are nesting scrollviews you might want to subclass the scrollview and override the built in check for UIControl. - (BOOL)touchesShouldCancelInContentView:(UIView *)view { if ([self isRunningiOS7OrLater]) { return YES; } return [super touchesShouldCancelInContentView:view]; }
-
Mika about 10 yearsThis is better than the accepted answer. One point I would add is that it's better to add this right before the cell is returned rather than after it's instantiated because setting up the cell can reset
setDelaysContentTouches
-
user about 10 years+1 Mikael, that made the difference for me. +1 for the answer because
respondsToSelector
is a much better solution than class comparison. -
bmeulmeester almost 10 yearsThis answer also helped me fix a bug with a regular UIScrollView. Thanks! 😁
-
Quentin over 9 yearsUnfortunately this code will not work in iOS8 as the UITableViewCellScrollView has been removed from the UITableViewCell view hierarchy.
-
Quentin over 9 yearsSee my answer: stackoverflow.com/a/26049216/194191 for a solution that works in iOS7 and iOS8
-
Gank over 9 yearsHow to write the function
initWithFrame
? PriceDetailTableView.m:66:18: No visible @interface for 'UITableViewController' declares the selector 'initWithFrame:'. My code:- (id) initWithFrame:(CGRect)frame style:(UITableViewStyle)style { self = [super initWithFrame:frame ]; self = [super initWithStyle:style]; return self; }
-
Gank over 9 years
- (id) initWithFrame:(CGRect)frame style:(UITableViewStyle)style { self=[super initWithFrame:frame style:style]; return self; }
PriceDetailTableView.m:80:17: No visible @interface for 'UITableViewController' declares the selector 'initWithFrame:style:' -
Gank over 9 yearsand UITableViewCell's initWithFrame will not enter in
-
Quentin over 9 years@Gank: I updated the post for a little more clarity. You need to implement
- (id)initWithFrame:(CGRect)frame
in your PriceDetailTableView and- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
in your PriceDetailTableViewCell. Does that make more sense? -
Donnit over 9 yearsYou made my day! Works like a charm!
-
Rusik about 9 yearsCategory? You sure? :)
-
SmileBot about 9 yearsActually, this is the cleanest answer on the page IMO and works as well in ios 7 as any of the others, and works properly in ios 8. That is, I'm getting ios 7 to only show the flash when the cell is selected as with all the other answers. Am I missing something?
-
SmileBot about 9 yearsOk, I got it, I see that you have to give the button a background color in ios 7 to get the flash but not in ios 8. Got it. Great answer!! Thanx.
-
Nailer about 9 yearsWorks as a charm to put in a subclass of UIButton. Much love <3
-
thomasdao almost 9 yearsHow to write it in Swift? I cannot do self = super.init(style: style, reuseIdentifier: reuseIdentifier) in Swift: Cannot assign to 'self' in a method. Also, view.class is not working
-
lenhhoxung almost 9 yearsTrue solution for storyboard and ios 8 and above
-
lenhhoxung almost 9 yearsGood solution, particularly if we are using UITableView on another view, not in a UIViewController (that case, delaysContentTouches doesn't work).
-
Abhishek almost 9 yearsWrite content of If (self) {} in awakeFromNib If you are using storyboard in both objective C and swift
-
Evren Bingøl almost 9 yearsThis is a great solution. All i found was examples based on view being a subclass of UIScrollView or TableView. I did not have anyof them as a parent class. Nothing else worked. this is great.
-
B-Man over 8 yearsWorked for me. Thanks yo! :) Perfect Solution
-
surfrider over 8 yearsThanks! It works, but it's a weird solution. I can't believe Apple didn't take care of this behaviour without inspecting of subviews.
-
pnizzle about 8 yearsNot sure why no one has tried this, but has worked for me. Question is why would the tableview, which is a scrollview subclass itself, have another scrollview inside of it. Apple :|
-
Leverin about 8 yearsWhy is this gem hidden here in the bottom?
-
antoine about 8 yearsThat solution works fine if you don't use the button selected state. The exact parameters for swift 2 are "touches: Set<UITouch>, withEvent event: UIEvent?".
-
RadioLog over 5 yearsFaced the same issue on iOS11. There only tableView.delaysContentTouches = NO was needed to fix the problem. Looks like they FINALLY fixed that (it seems to be present on iOS10 though)
-
iadcialim24 over 3 yearsIm facing this in iOS14