UIButton在iOS7中点击时不显示突出显示


71

我看过很多关于类似问题的文章,但没有一个完全符合或解决此问题。从iOS 7开始,每当我UIButtonUITableViewCell或什至于footerview中添加a时,它都可以“正常”运行,这意味着它会收到目标操作,但不会显示通常在您点击a时出现的小亮点UIButton。它使UI看起来很时髦,没有显示按钮对触摸的反应。

我很确定这算是iOS7中的错误,但是有没有人找到解决方案或可以帮助我找到一个解决方案:)

编辑:我忘了提到,如果我长按该按钮,它将突出显示,但是不能像添加到标准视图中那样快速点击。

码:

创建按钮:

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

我测试过的东西:

//删除手势识别器UITableView,以防它们妨碍您的操作。

for (UIGestureRecognizer *recognizer in self.tableView.gestureRecognizers) {
   recognizer.enabled = NO;
}

//从单元格中删除手势

for (UIGestureRecognizer *recognizer in self.contentView.gestureRecognizers) {
       recognizer.enabled = NO;
    }

//这显示了一点点轻触,但这不是所需的外观

button.showsTouchWhenHighlighted = YES;

将setTitleColor用于突出显示状态。(不确定,但也可以尝试UIButtonTypeCustom)
msk

那不能解决问题。如果我长按该按钮,它将变为突出显示的颜色,而不仅仅是单击即可
Eric

仅在常规ViewController中而不是在滚动视图或表下的子类UIButton遇到此问题。有任何想法吗?
本·惠勒

Answers:


91

在该表视图中,您只需添加此属性。

tableview.delaysContentTouches = NO;

并在启动单元格后添加cellForRowAtIndexPath,只需添加以下代码即可。单元的结构在iOS 6和iOS 7中明显不同
。iOS7在UITableViewCell和内容视图之间有一个控件UITableViewCellScrollView。

for (id obj in cell.subviews)
{
    if ([NSStringFromClass([obj class]) isEqualToString:@"UITableViewCellScrollView"])
    {
        UIScrollView *scroll = (UIScrollView *) obj;
        scroll.delaysContentTouches = NO;
        break;
    }
}

3
你是个天才,谢谢!我编辑了子视图循环,以使其更富未来性。发布编辑内容,因此一旦获得批准,有望帮助其他人
Eric

当然,感谢您发布此信息。我希望这对其他人也有帮助。
subramani 2013年

谢啦。我尝试编写条件,([obj isKindOfClass:[UITableViewCellScrollView class]])但引发错误,说它不知道名为的类UITableViewCellScrollView。为什么?
Fred Collins

2
@RyanRomanchuk我已经使用了多个按钮。我知道,如果滚动的开始位置(您在其中按下)是一个按钮,它的确会停止滚动。这是因为现在触摸被发送到按钮而不是滚动视图。我仍然这样做是因为我认为显示按钮高亮比使滚动工作更重要,即使他们开始在按钮上滚动也是如此。不过,就我而言,可能就是这样
Eric

5
不幸的是,由于UITableViewCellScrollView已从UITableViewCell视图层次结构中删除,因此此代码在iOS8中不起作用。
昆汀2014年

40

从iOS 8开始,我们需要对UITableView子视图(表包含隐藏的UITableViewWrapperView滚动视图)应用相同的技术。不再需要迭代UITableViewCell子视图。

for (UIView *currentView in tableView.subviews) {
    if ([currentView isKindOfClass:[UIScrollView class]]) {
        ((UIScrollView *)currentView).delaysContentTouches = NO;
        break;
    }
}

这个答案应该与此问题联系在一起。


谢谢!它有效,但这是一个奇怪的解决方案。我不敢相信,如果不检查子视图,苹果就不会照顾这种行为。
2015年

1
在iOS11上也面临同样的问题。仅需要tableView.delaysContentTouches = NO即可解决此问题。看起来他们终于解决了该问题(尽管它似乎在iOS10上存在)
RadioLog

我在iOS14中面临此问题
iori24 '20

27

我试图将其添加到可接受的答案中,但从未成功。这是一种关闭单元格delaysContentTouches属性的更安全的方法,因为它不查找特定的类,而是查找对选择器有响应的任何内容。

在单元格中:

for (id obj in self.subviews) {
     if ([obj respondsToSelector:@selector(setDelaysContentTouches:)]) {
          [obj setDelaysContentTouches:NO];
     }
}

在TableView中:

self.tableView.delaysContentTouches = NO;

3
这比公认的答案更好。我要补充的一点是,最好在返回单元之前而不是在实例化之后添加此权限,因为设置单元可以重置setDelaysContentTouches
Mika 2014年

1
+1 Mikael,这让我与众不同。+1是答案,因为respondsToSelector比类比较好得多。
用户

18

对于适用于iOS7和iOS8的解决方案,请创建自定义UITableView子类和自定义UITableViewCell子类。

使用此样本UITableViewinitWithFrame:

- (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;
}

使用此样本UITableViewCellinitWithStyle: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;
}

如何编写函数initWithFrame?PriceDetailTableView.m:66:18:'UITableViewController'的可见@interface没有声明选择器'initWithFrame:'。我的代码:- (id) initWithFrame:(CGRect)frame style:(UITableViewStyle)style { self = [super initWithFrame:frame ]; self = [super initWithStyle:style]; return self; }
Gank,2014年

- (id) initWithFrame:(CGRect)frame style:(UITableViewStyle)style { self=[super initWithFrame:frame style:style]; return self; }PriceDetailTableView.m:80:17:“ UITableViewController”没有可见的@interface声明选择器“ initWithFrame:style:”
Gank 2014年

和UITableViewCell的initWithFrame将不会输入
Gank 2014年

1
@Gank:我更新了帖子以使内容更加清晰。你需要实现- (id)initWithFrame:(CGRect)frame你的PriceDetailTableView,并- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier在你的PriceDetailTableViewCell。这更有意义吗?
昆汀2014年

你让我今天一整天都感觉很好!奇迹般有效!
Donnit 2015年

16

我要解决的问题是使用以下代码的UIButton类别:

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

当我在UITableViewCell中按下按钮时,该按钮可以正确反应,并且UITableView的行为正常,因为delaysContentTouches它不是强制性的。


2
类别?你确定?:)
Rusik 2015年

1
用作插入UIButton子类的魅力。大爱<3
Nailer

1
好的解决方案,尤其是如果我们在另一个视图上而不是在UIViewController中使用UITableView时(这种情况下,delaysContentTouches不起作用)。
lenhhoxung

1
这是一个很好的解决方案。我发现的只是基于view的示例,而view是UIScrollView或TableView的子类。我没有他们作为家长班。没有其他工作。这很棒。
EvrenBingøl2015年

11

这是Roman B在Swift 2中的回答:

for view in tableView.subviews {
    if view is UIScrollView {
        (view as? UIScrollView)!.delaysContentTouches = false
        break
    }
}

3
不知道为什么没有人尝试过,但是为我工作。问题是,为什么tableview本身是一个scrollview子类,却在其中具有另一个scrollview。苹果:|
pnizzle '16

1
为什么将此宝石隐藏在底部?
勒沃林

5
    - (void)viewDidLoad
{

    [super viewDidLoad];


    for (id view in self.tableView.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;
        }
    }
}

在此处输入图片说明


实际上,这是IMO页上最干净的答案,并且在ios 7中的工作原理与其他任何方法都一样,并且在ios 8中也可以正常工作。也就是说,我要使ios 7仅在选择单元时才显示闪存和所有其他答案一样。我想念什么吗?
smileBot 2015年

好的,我明白了,我发现您必须在ios 7中为按钮提供背景颜色才能获得闪光,而在ios 8中则不能。好答案!!谢谢
smileBot 2015年

情节
提要

5

我遇到了类似的问题,即UITableViewCell中的纯文本UIButton在触摸时未突出显示。对我来说,解决此问题的原因是将buttonType从“ Custom”更改回“ System”。

将delaysContentTouches设置为NO可以解决同一UITableViewCell中仅图像的UIButton的问题。

self.tableView.delaysContentTouches = NO;

在此处输入图片说明


4

这是RaphaëlPinto上面回答的Swift版本。别忘了也投票给他:)

override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
    super.touchesBegan(touches, withEvent: event)
    NSOperationQueue.mainQueue().addOperationWithBlock { () -> Void in self.highlighted = true }
}

override func touchesCancelled(touches: NSSet!, withEvent event: UIEvent!) {
    super.touchesCancelled(touches, withEvent: event)
    let time = dispatch_time(DISPATCH_TIME_NOW, Int64(0.1 * Double(NSEC_PER_SEC)))
    dispatch_after(time, dispatch_get_main_queue()) {
        self.setDefault()
    }
}

override func touchesEnded(touches: NSSet, withEvent event: UIEvent) {
    super.touchesEnded(touches, withEvent: event)
    let time = dispatch_time(DISPATCH_TIME_NOW, Int64(0.1 * Double(NSEC_PER_SEC)))
    dispatch_after(time, dispatch_get_main_queue()) {
        self.setDefault()
    }
}

func setDefault() {
    NSOperationQueue.mainQueue().addOperationWithBlock { () -> Void in self.highlighted = false }
}

1
如果您不使用按钮选择状态,则该解决方案效果很好。swift 2的确切参数是“ touches:Set <UITouch>,withEvent事件:UIEvent?”。
antoine

3

Swift中的解决方案,仅适用于iOS8(需要在iOS7的每个单元上进行额外的工作):

//
//  NoDelayTableView.swift
//  DivineBiblePhone
//
//  Created by Chris Hulbert on 30/03/2015.
//  Copyright (c) 2015 Chris Hulbert. All rights reserved.
//
//  This solves the delayed-tap issue on buttons on cells.

import UIKit

class NoDelayTableView: UITableView {

    required init(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)

        delaysContentTouches = false

        // This solves the iOS8 delayed-tap issue.
        // http://stackoverflow.com/questions/19256996/uibutton-not-showing-highlight-on-tap-in-ios7
        for view in subviews {
            if let scroll = view as? UIScrollView {
                scroll.delaysContentTouches = false
            }
        }
    }

    override func touchesShouldCancelInContentView(view: UIView!) -> Bool {
        // So that if you tap and drag, it cancels the tap.
        return true
    }

}

要使用,您所要做的就是NoDelayTableView在您的情节提要中将类更改为。

我可以确认,在iOS8中,放置在单元格中的contentView内部的按钮现在可以立即突出显示。


为我工作。谢谢哟!:)完美解决方案
B-Man

2

克里斯·哈里森的答案略作修改。Swift 2.3:

class HighlightButton: UIButton {
    override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
        super.touchesBegan(touches, withEvent: event)
        NSOperationQueue.mainQueue().addOperationWithBlock { _ in self.highlighted = true }
    }

    override func touchesCancelled(touches: Set<UITouch>?, withEvent event: UIEvent?) {
        super.touchesCancelled(touches, withEvent: event)
        setDefault()
    }

    override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
        super.touchesEnded(touches, withEvent: event)
        setDefault()
    }

    private func setDefault() {
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(0.1 * Double(NSEC_PER_SEC))), dispatch_get_main_queue()) {
            NSOperationQueue.mainQueue().addOperationWithBlock { _ in self.highlighted = false }
        }
    }
}

1

对我来说,被接受的答案有些“不可行”。

最后,我将波纹管代码添加到uibutton类别(/子类)中,并且可以实现百分之一百的效果。

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{

self.backgroundColor = [UIColor greenColor];
[UIView animateWithDuration:0.05 delay:0 options:UIViewAnimationOptionCurveLinear animations:^{
    self.backgroundColor = [UIColor clearColor];

} completion:^(BOOL finished)
 {
 }];
[super touchesBegan:touches withEvent:event];

}

0

我写了一个类别扩展名,UITableViewCell以使此问题更容易解决。它与接受的答案基本上具有相同的功能,除了我从UITableViewCell contentView

我考虑了一种完全“自动的”解决方案,该解决方案将使添加到UITableView集合中的所有单元格的delaysContentTouches状态与所有者UITableView的状态相匹配delaysContentTouches。为了使这项工作有效,我要么不得不烦恼UITableView,要么要求开发人员使用UITableView子类。既不希望我也选择此解决方案,我觉得它更简单,更灵活。

类别扩展和示例工具在这里:

https://github.com/TomSwift/UITableViewCell-TS_delaysContentTouches

使用起来非常简单:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    // using static cells from storyboard...
    UITableViewCell* cell = [super tableView: tableView cellForRowAtIndexPath: indexPath];

    cell.ts_delaysContentTouches = NO;

    cell.selectionStyle = UITableViewCellSelectionStyleNone;

    return cell;
}

这是类别的代码:

@interface UITableViewCell (TS_delaysContentTouches)

@property (nonatomic, assign) BOOL ts_delaysContentTouches;

@end

@implementation UITableViewCell (TS_delaysContentTouches)

- (UIScrollView*) ts_scrollView
{
    id sv = self.contentView.superview;
    while ( ![sv isKindOfClass: [UIScrollView class]] && sv != self )
    {
        sv = [sv superview];
    }

    return sv == self ? nil : sv;
}

- (void) setTs_delaysContentTouches:(BOOL)delaysContentTouches
{
    [self willChangeValueForKey: @"ts_delaysContentTouches"];

    [[self ts_scrollView] setDelaysContentTouches: delaysContentTouches];

    [self didChangeValueForKey: @"ts_delaysContentTouches"];
}

- (BOOL) ts_delaysContentTouches
{
    return [[self ts_scrollView] delaysContentTouches];
}

@end

因此,基本上设置一个单元格ts_delaysContentTouches可以设置整个tableViews delaysContentTouches正确吗?因此,这将不允许动态单元格设置,而是将整个tableview设置为一个整体?
埃里克

@Eric-不 我有意识地决定不要同时设置tableview delaysContentTouches。我认为这是一个意外的副作用,因为它将更改层次结构中上游的视图。您仍然需要设置tableView.delaysContentTouches = NO。
TomSwift 2014年

0

由于objc是动态的,并且scrollView是唯一响应delaysContentTouches的类,因此这对于ios 7和ios 8均适用(将其放在tableViewController的早期位置,如awakeFromNib):

for (id view in self.tableView.subviews)
    {
        if ([view respondsToSelector:@selector(delaysContentTouches)]) {
            UIScrollView *scrollView = (UIScrollView *)view;
            scrollView.delaysContentTouches = NO;
            break;
        }
}

您可能还需要通过选择viewController内部的表来关闭情节提要或笔尖中的“ delaysContentTouches”。顺便说一句,如果您在viewController中使用tableView,那么这可能在ios 7上不起作用,至少我无法使其正常工作。


0

该解决方案对我不起作用,我修复了TableView的子类化并实现了这两种方法

- (instancetype)initWithCoder:(NSCoder *)coder{
   self = [super initWithCoder:coder];
   if (self) {
     for (id obj in self.subviews) {
      if ([obj respondsToSelector:@selector(setDelaysContentTouches:)]){
            [obj performSelector:@selector(setDelaysContentTouches:) withObject:NO];
      }
     }
   }
   return self;
}

- (BOOL)delaysContentTouches{
   return NO;
}

0

适用于iOS 7和8的Swift解决方案:

首先,我编写了一个实用程序函数:

class func classNameAsString(obj: AnyObject) -> String {
    return _stdlib_getDemangledTypeName(obj).componentsSeparatedByString(".").last!
}

然后我子类化UITableView并实现此:

required init(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)

    for view in self.subviews {
        if (Utility.classNameAsString(view) == "UITableViewWrapperView") {
            if view.isKindOfClass(UIScrollView) {
                var scroll = (view as UIScrollView)
                scroll.delaysContentTouches = false
            }
            break
        }
    }
}

我还继承了UITableViewCell并实现了这一点:

required init(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)

    for view in self.subviews {
        if (Utility.classNameAsString(view) == "UITableViewCellScrollView") {
            if view.isKindOfClass(UIScrollView) {
                var scroll = (view as UIScrollView)
                scroll.delaysContentTouches = false
            }

        }
    }
}

在我的情况下,init(coder :)将运行。请在您的init函数中放置调试点,以了解哪个init函数将运行,然后使用上面的代码使其起作用。希望可以帮助别人。


0

在Swift 3中,可以在UITableViewCell上使用此UIView扩展。最好在该cellForRowAt方法中。

func removeTouchDelayForSubviews() {
    for subview in subviews {
        if let scrollView = subview as? UIScrollView {
            scrollView.delaysContentTouches = false
        } else {
            subview.removeTouchDelayForSubviews()
        }
    }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.