UIButton在iOS7中没有显示突出显示

Posted

技术标签:

【中文标题】UIButton在iOS7中没有显示突出显示【英文标题】:UIButton not showing highlight on tap in iOS7 【发布时间】:2013-10-08 19:50:23 【问题描述】:

我查看了大量关于类似事物的帖子,但没有一个完全符合或解决此问题。从 ios 7 开始,每当我将 UIButton 添加到 UITableViewCell 甚至添加到页脚视图时,它都会“正常”工作,这意味着它会接收目标操作,但它不会显示通常在您点击 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 ) 这并不能解决问题。如果我长按按钮,它将变为突出显示的颜色,但不仅仅是单击 在普通 ViewController 中,而不是在滚动视图或表格下的子类 UIButton 存在此问题。有什么想法吗? 【参考方案1】:

在那个 tableview 中你只需添加这个属性。

tableview.delaysContentTouches = NO;

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

for (id obj in cell.subviews)

    if ([NSStringFromClass([obj class]) isEqualToString:@"UITableViewCellScrollView"])
    
        UIScrollView *scroll = (UIScrollView *) obj;
        scroll.delaysContentTouches = NO;
        break;
    

【讨论】:

你真是个天才,谢谢!我编辑了您的子视图循环,以使其更具未来性。发布了编辑,因此一旦获得批准,希望能帮助其他人 当然,感谢您发布此信息。我希望这对其他人也有帮助。 谢谢哥们。我试图编写条件([obj isKindOfClass:[UITableViewCellScrollView class]]),但它引发了一个错误,指出它不知道名为UITableViewCellScrollView 的类。为什么? @RyanRomanchuk 我已经将它与多个按钮一起使用。我知道如果滚动的开始(您点击的地方)是一个按钮,这确实会停止滚动。这是因为现在触摸被发送到按钮而不是滚动视图。我仍然这样做是因为我认为显示按钮突出显示比使滚动工作更重要,即使他们在按钮上开始滚动也是如此。这可能只是我的情况 不幸的是,由于 UITableViewCellScrollView 已从 UITableViewCell 视图层次结构中删除,因此此代码在 iOS8 中不起作用。【参考方案2】:

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

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

This answer 应该与这个问题相关联。

【讨论】:

谢谢!它有效,但这是一个奇怪的解决方案。我不敢相信 Apple 在不检查子视图的情况下没有处理这种行为。 在 iOS11 上遇到了同样的问题。只有 tableView.delaysContentTouches = NO 需要解决问题。看起来他们终于解决了这个问题(虽然它似乎出现在 iOS10 上) 我在 iOS14 中面临这个问题【参考方案3】:

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

在单元格中:

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

在表格视图中:

self.tableView.delaysContentTouches = NO;

【讨论】:

这比接受的答案要好。我要补充的一点是,最好在返回单元格之前而不是在实例化之后添加它,因为设置单元格可以重置setDelaysContentTouches +1 Mikael,这对我来说很重要。为答案 +1,因为 respondsToSelector 是比类比较更好的解决方案。【参考方案4】:

对于同时适用于 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; - (id) initWithFrame:(CGRect)frame style:(UITableViewStyle)style self=[super initWithFrame:frame style:style]; return self; PriceDetailTableView.m:80:17: 'UITableViewController' 没有可见的@interface 声明选择器'initWithFrame:style:' 和UITableViewCell的initWithFrame不会进入 @Gank:我更新了帖子以使内容更加清晰。您需要在 PriceDetailTableView 中实现 - (id)initWithFrame:(CGRect)frame,在 PriceDetailTableViewCell 中实现 - (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier。这更有意义吗? 你成就了我的一天!像魅力一样工作!【参考方案5】:

我为解决这个问题所做的是使用以下代码的 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 未被强制。

【讨论】:

类别?你确定? :) 作为一个魅力放入 UIButton 的子类。多爱 很好的解决方案,特别是如果我们在另一个视图上使用 UITableView,而不是在 UIViewController 中(这种情况下,delaysContentTouches 不起作用)。 这是一个很好的解决方案。我发现的只是基于视图的示例,它是 UIScrollView 或 TableView 的子类。我没有他们中的任何一个作为父类。没有其他工作。这很棒。【参考方案6】:

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

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

【讨论】:

不知道为什么没有人尝试过这个,但对我有用。问题是为什么作为滚动视图子类本身的表格视图内部会有另一个滚动视图。苹果:| 这颗宝石为什么藏在底部?【参考方案7】:
    - (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与所有其他答案一样,当单元格被选中时显示闪烁。我错过了什么吗? 好的,我明白了,我看到你必须在 ios 7 中为按钮设置背景颜色才能获得闪光灯,但在 ios 8 中没有。明白了。很好的答案!谢谢。 故事板和ios 8及以上版本的真正解决方案【参考方案8】:

我在 UITableViewCell 中的纯文本 UIButton 在触摸时未突出显示时遇到了类似的问题。为我解决的问题是将 buttonType 从 Custom 改回 System。

将 delaysContentTouches 设置为 NO 对同一 UITableViewCell 中的仅图像 UIButton 起到了作用。

self.tableView.delaysContentTouches = NO;

【讨论】:

【参考方案9】:

这是上述 Raphaël Pinto 回答的 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 

【讨论】:

如果您不使用按钮选择状态,该解决方案可以正常工作。 swift 2 的确切参数是“touches: Set, withEvent event: UIEvent?”。【参考方案10】:

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://***.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 内的按钮现在会立即突出显示。

【讨论】:

为我工作。谢谢哟! :) 完美解决方案【参考方案11】:

Chris Harrison's answer 的略微修改版本。斯威夫特 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 
        
    

【讨论】:

【参考方案12】:

接受的答案对我来说在某些“点击”时不起作用。

最后我在 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];


【讨论】:

【参考方案13】:

我在UITableViewCell 上写了一个类别扩展,以使这个问题易于解决。它与接受的答案基本相同,除了我从UITableViewCell contentView 向上走(而不是向下)视图层次结构。

我考虑了一个完全“自动”的解决方案,它可以使添加到UITableView 的所有单元格设置它们的delaysContentTouches 状态以匹配拥有UITableViewdelaysContentTouches 状态。为了完成这项工作,我必须调配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。【参考方案14】:

由于 objc 是动态的,并且 scrollView 是唯一响应 delaysContentTouches 的类,这应该适用于 ios 7 和 8(将它放在 tableViewController 早期的某个位置,例如 awakeFromNib):

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

您可能还需要通过选择 viewController 中的表格来关闭情节提要或 nib 中的“delaysContentTouches”。顺便说一句,如果您在 viewController 中使用 tableView,这可能不适用于 ios 7,至少我无法让它工作。

【讨论】:

【参考方案15】:

那个解决方案对我不起作用,我修复子类化 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;

【讨论】:

【参考方案16】:

适用于 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 函数将运行,然后使用上面的代码使其工作。 希望对某人有所帮助。

【讨论】:

【参考方案17】:

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

func removeTouchDelayForSubviews() 
    for subview in subviews 
        if let scrollView = subview as? UIScrollView 
            scrollView.delaysContentTouches = false
         else 
            subview.removeTouchDelayForSubviews()
        
    

【讨论】:

以上是关于UIButton在iOS7中没有显示突出显示的主要内容,如果未能解决你的问题,请参考以下文章

屏幕左侧的 UIButton(在 iOS 7 导航滑动区域中)未突出显示

如何创建具有渐变和突出显示的 UIButton?

什么控制事件开始和结束突出显示UIButton的状态

ios7 datepicker当前日期未突出显示

如何禁用 UIButton 突出显示操作

针对突出显示的背景设置 UIButton 突出显示的标题文本,从而产生暗淡的文本