简体   繁体   English

UIButton 在 iOS7 中点击时不显示高亮

[英]UIButton not showing highlight on tap in iOS7

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 a UITableViewCell 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 a UIButton .从 iOS 7 开始,每当我将UIButton添加到UITableViewCell或什至页脚视图时,它都会“正常”工作,这意味着它会接收目标操作,但它不会显示通常在您点击UIButton时发生的小亮点。 It makes the UI look funky not showing the button react to touch.它使 UI 看起来很时髦,不显示按钮对触摸的反应。

I'm pretty sure this counts as a bug in iOS7, but has anyone found a solution or could help me find one :)我很确定这算作 iOS7 中的一个错误,但有没有人找到解决方案或可以帮我找到一个 :)

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. //删除UITableView上的手势识别器,以防它们UITableView路。

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;

In that tableview you just add this property.在该 tableview 中,您只需添加此属性。

tableview.delaysContentTouches = NO;

And add in cellForRowAtIndexPath after you initiate the cell you just add below code.并在您启动您刚刚添加以下代码的单元格后添加 cellForRowAtIndexPath。 The structure of the cell is apparently different in iOS 6 and iOS 7.单元格的结构在 iOS 6 和 iOS 7 中明显不同。
iOS 7 we have one control UITableViewCellScrollView In between UITableViewCell and content View. iOS 7 我们有一个控件 UITableViewCellScrollView 在 UITableViewCell 和内容视图之间。

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

Since iOS 8 we need to apply the same technique to UITableView subviews (table contains a hidden UITableViewWrapperView scroll view).从 iOS 8 开始,我们需要将相同的技术应用于 UITableView 子视图(表包含一个隐藏的 UITableViewWrapperView 滚动视图)。 There is no need iterate UITableViewCell subviews anymore.不再需要迭代 UITableViewCell 子视图。

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

This answer should be linked with this question. 这个答案应该与这个问题相关联。

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.这是关闭单元格 delaysContentTouches 属性的更安全的方法,因为它不查找特定类,而是查找任何响应选择器的内容。

In Cell:在单元格中:

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

In TableView:在表视图中:

self.tableView.delaysContentTouches = NO;

For a solution that works in both iOS7 and iOS8, create a custom UITableView subclass and custom UITableViewCell subclass.对于适用于 iOS7 和 iOS8 的解决方案,请创建自定义UITableView子类和自定义UITableViewCell子类。

Use this sample UITableView 's initWithFrame:使用这个示例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;
}

Use this sample UITableViewCell 's initWithStyle:reuseIdentifier:使用这个示例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;
}

What I did to solve the problem was a category of UIButton using the following code :我为解决这个问题所做的是使用以下代码的 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; }];
}

the button reacts correctly when I press on it in a UITableViewCell, and my UITableView behaves normally as the delaysContentTouches isn't forced.当我在 UITableViewCell 中按下按钮时,按钮会正确反应,并且我的 UITableView 行为正常,因为delaysContentTouches不是强制的。

Here's Roman B's answer in Swift 2:这是 Roman B 在 Swift 2 中的回答:

for view in tableView.subviews {
    if view is UIScrollView {
        (view as? UIScrollView)!.delaysContentTouches = false
        break
    }
}
    - (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;
        }
    }
}

在此处输入图片说明

I was having similar issues with a text-only UIButton in a UITableViewCell not highlighting upon touch.我在 UITableViewCell 中的纯文本 UIButton 遇到了类似的问题,没有在触摸时突出显示。 What fixed it for me was changing the buttonType from Custom back to System.对我来说修复它的是将 buttonType 从自定义更改回系统。

Setting delaysContentTouches to NO did the trick for the image-only UIButton in the same UITableViewCell.将 delaysContentTouches 设置为 NO 对同一 UITableViewCell 中的纯图像 UIButton 起到了作用。

self.tableView.delaysContentTouches = NO;

在此处输入图片说明

This is a Swift version of Raphaël Pinto's answer above.这是上述 Raphaël Pinto 答案的Swift版本。 Don't forget to upvote him too :)也不要忘记给他点赞:)

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

Solution in Swift, iOS8 only (needs the extra work on each of the cells for iOS7):仅适用于 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
    }

}

To use, all you have to do is change the class to NoDelayTableView in your storyboard.要使用,您所要做的就是在故事板中将类更改为NoDelayTableView

I can confirm that in iOS8, buttons placed inside a contentView in a cell now highlight instantly.我可以确认在 iOS8 中,放置在单元格中 contentView 中的按钮现在会立即突出显示。

Slightly modified version of Chris Harrison's answer . Chris Harrison 的答案略有修改。 Swift 2.3:斯威夫特 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 }
        }
    }
}

The accepted answer did not work at some "taps" for me .接受的答案对我来说在某些“点击”时不起作用。

Finally I add the bellow code in a uibutton category(/subclass),and it works a hundred percent.最后,我在 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];

}

I wrote a category extension on UITableViewCell to make this issue simple to address.我在UITableViewCell上写了一个类别扩展来简化这个问题的解决。 It does basically the same thing as the accepted answer except I walk up the view hierarchy (as opposed to down) from the UITableViewCell contentView .除了我从UITableViewCell contentView向上走视图层次结构(而不​​是向下)之外,它与接受的答案基本相同。

I considered a fully "automagic" solution that would make all cells added to a UITableView set their delaysContentTouches state to match the owning UITableView 's delaysContentTouches state.我考虑了一个完全“自动”的解决方案,它可以使添加到UITableView所有单元格设置它们的delaysContentTouches状态以匹配拥有UITableViewdelaysContentTouches状态。 To make this work I'd have to either swizzle UITableView , or require the developer to use a UITableView subclass.为了完成这项工作,我必须调整UITableView ,或者要求开发人员使用UITableView子类。 Not wanting to require either I settled on this solution which I feel is simpler and more flexible.不想要求我选择了这个我觉得更简单、更灵活的解决方案。

Category extension and sample harness here:类别扩展和示例线束在这里:

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

It's dead-simple to use:使用起来非常简单:

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

Here's the code for the category:这是该类别的代码:

@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

Since objc is dynamic, and scrollView is the only class that responds to delaysContentTouches, this should work for both ios 7 and 8 (put it somewhere early in your tableViewController, like awakeFromNib):由于 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;
        }
}

You may also have to turn off "delaysContentTouches" in your storyboard or nib by selecting the table inside your viewController.您可能还需要通过选择 viewController 中的表格来关闭故事板或笔尖中的“delaysContentTouches”。 BTW, this might not work on ios 7 if you're using a tableView inside a viewController, at least I couldn't get it to work.顺便说一句,如果您在 viewController 中使用 tableView,这可能不适用于 ios 7,至少我无法让它工作。

That solution for me doesn't work, I fixed subclassing TableView and implementing these two methods那个解决方案对我不起作用,我修复了子类 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;
}

Solution in Swift for iOS 7 and 8:适用于 iOS 7 和 8 的 Swift 解决方案:

First I wrote a utility function:首先我写了一个效用函数:

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

then I subclass UITableView and implement this:然后我子类化 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
        }
    }
}

I also subclass UITableViewCell and implement this:我还继承了 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
            }

        }
    }
}

In my case the init(coder:) will run.在我的情况下,init(coder:) 将运行。 Please put debug point in your init functions to know which init function will run, then using the code above to make it work.请在您的 init 函数中放置调试点以了解将运行哪个 init 函数,然后使用上面的代码使其工作。 Hope to help someone.希望能帮助某人。

In Swift 3 this UIView extension can be used on the UITableViewCell.在 Swift 3 中,这个 UIView 扩展可以用于 UITableViewCell。 Preferably in the cellForRowAt method.最好在cellForRowAt方法中。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM