UIButton не показывает подсветку при нажатии на iOS7

Я просмотрел массу сообщений по подобным вещам, но ни один из них не соответствует или не устраняет эту проблему. Начиная с iOS 7, всякий раз, когда я добавляю UIButton в UITableViewCell или даже в footerview, он работает "отлично", что означает, что он получает целевое действие, но он не показывает малое выделение, которое обычно происходит, когда вы нажимаете UIButton. Это заставляет пользовательский интерфейс выглядеть фанки, не показывая, как кнопка реагирует на касание.

Я уверен, что это считается ошибкой в ​​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;

Ответ 1

В этом представлении таблицы вы просто добавляете это свойство.

tableview.delaysContentTouches = NO;

И добавьте в cellForRowAtIndexPath после того, как вы инициируете ячейку, которую вы просто добавляете под кодом. Структура ячейки, по-видимому, отличается в iOS 6 и iOS 7.
iOS 7 у нас есть один элемент управления UITableViewCellScrollView Между UITableViewCell и представлением содержимого.

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

Ответ 2

Так как iOS 8 нам нужно применить ту же технику к subviews UITableView (таблица содержит скрытый вид прокрутки UITableViewWrapperView). Нет необходимости перебирать подпрограммы UITableViewCell.

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

Этот ответ должен быть связан с этим вопросом.

Ответ 3

Я попытался добавить это к принятому ответу, но он так и не прошел. Это гораздо более безопасный способ отключить свойство delaymentsContentTouches ячейки, поскольку он не ищет определенный класс, а скорее все, что реагирует на селектор.

В ячейке:

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

В TableView:

self.tableView.delaysContentTouches = NO;

Ответ 4

Для решения, которое работает как в iOS7, так и в iOS8, создайте собственный подкласс UITableView и пользовательский подкласс UITableViewCell.

Используйте этот образец UITableView initWithFrame:

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];

    if (self)
    {
        // iterate over all the UITableView 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;
}

Используйте этот образец UITableViewCell initWithStyle:reuseIdentifier:

- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier 
{
    self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];

    if (self)
    {
        // iterate over all the UITableViewCell 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;
}

Ответ 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 не принудительно.

Ответ 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;
        }
    }
}

enter image description here

Ответ 8

Это версия 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 }
}

Ответ 9

Решение в 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 в ячейке, теперь выделяются мгновенно.

Ответ 10

У меня были подобные проблемы с текстовым UIButton в UITableViewCell, не выделяющимся при прикосновении. Для меня это изменилось с помощью кнопки buttonType от Custom до System.

Установка задержекContentTouches to NO сделала трюк для UIButton только для изображения в том же UITableViewCell.

self.tableView.delaysContentTouches = NO;

введите описание изображения здесь

Ответ 11

Немного измененная версия Крис Харрисон ответ. 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 }
        }
    }
}

Ответ 12

Я написал расширение категории на UITableViewCell, чтобы упростить эту проблему. Это в основном то же самое, что и принятый ответ, за исключением того, что я поднимаю иерархию представления (в отличие от вниз) от UITableViewCell contentView.

Я рассмотрел полностью "автоматическое" решение, которое сделало бы все ячейки, добавленные в UITableView, установить их состояние delaysContentTouches в соответствии с состоянием UITableView delaysContentTouches. Для выполнения этой работы мне придется либо swizzle 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

Ответ 13

Принятый ответ не работал на некоторых "кранах" для меня.

Наконец, я добавляю следующий код в категорию uibutton (/subclass), и он работает на сто процентов.

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

}

Ответ 14

Поскольку objc является динамическим, и scrollView является единственным классом, который реагирует на задержки .ContentTouches, это должно работать как для 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;
        }
}

Вам также может потребоваться отключить "delayaysContentTouches" в вашем раскадровке или кегли, выбрав таблицу внутри вашего viewController. Кстати, это может не работать на ios 7, если вы используете tableView внутри viewController, по крайней мере, я не мог заставить его работать.

Ответ 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

Решение в Swift для iOS 7 и 8:

Сначала я написал служебную функцию:

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