IOS - событие Delayed "Touch Down" для UIButton в UITableViewCell

У меня есть пользовательский UITableViewCell, который инициализируется следующим:

- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
    self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
    if (self) {
        NSArray *nibArray = [[NSBundle mainBundle] loadNibNamed:@"CustomCell" owner:self options:nil];
        self = [nibArray objectAtIndex:0];

        [self setSelectionStyle:UITableViewCellSelectionStyleNone];

        [self.downButton setBackgroundImage:[UIImage imageNamed:@"button"] forState:UIControlStateNormal];
        [self.downButton setBackgroundImage:[UIImage imageNamed:@"buttonSelected"] forState:UIControlStateHighlighted];
    }
    return self;
}

Кнопка отображается правильно, с соответствующим фоновым изображением, но выделенное изображение сразу не появляется, когда кнопка нажата/нажата. Вместо этого вы должны удерживать его в течение секунды или двух до того, как произойдет изменение. С другой стороны, отмена кнопки имеет мгновенное изменение обратно к исходному фоновому изображению.

Пытаясь смягчить запоздалое изменение при переключении на выделенный снимок, я внес изменения следующим образом:

- (IBAction)downDown:(id)sender {
    [self.downButton setBackgroundColor:[UIColor redColor]];
}

Метод выше установлен для "Touch Down" (против более распространенного "Touch Up Inside" ), и я удалил setBackgroundImage:forState: для выделенного состояния. Такая же проблема, как упоминалось выше. Цвет в конечном итоге изменится на красный, но только после нажатия и удерживания кнопки на секунду или два.

У меня есть метод для кнопки, которая вызывается, когда происходит "Touch Up Inside", и этот метод выполняется без проблем - независимо от того, быстро ли я нажимаю кнопку или нажимаю и удерживаю ее на некоторое время перед выпуском.

Итак, почему задержка для "Touch Down" или UIControlStateHighlighted? Я пытаюсь предоставить мгновенную обратную связь с пользователем, чтобы показать, что кнопка нажата.

Я могу предоставить больше кода, если это необходимо, но это единственные биты, которые имеют какое-либо отношение к внешнему виду.

Ответ 1

Это вызвано свойством UIScrollView delaysContentTouches.

Достаточно было просто установить это свойство в NO для самого UITableView, но это будет работать только для подзонов таблицы, которые не заключены в оболочку в другой UIScrollView.

UITableViewCells содержит внутренний просмотр прокрутки в iOS 7, поэтому вам нужно будет изменить значение этого свойства на уровне ячейки для всех ячеек с кнопками в них.

Вот что вам нужно сделать:

1.in viewDidLoad или где-нибудь подобное, как только ваш UITableView был инициализирован, поместите это в:

self.tableView.delaysContentTouches = NO;

2. для поддержки iOS 7 в методе инициализации для UITableViewCell (initWithStyle:reuseIdentifier: или initWithCoder: для NIB) вставьте это в конец:

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

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

Ответ 2

Решение Swift 3 (для iOS8 +):

Во-первых, отключите задержки в UITableView после того, как она успешно загружена, например, внутри метода viewDidLoad():

someTableView.delaysContentTouches = false

Затем отключите задержки в просмотрах прокрутки, содержащихся внутри UITableView:

for case let scrollView as UIScrollView in someTableView.subviews {
    scrollView.delaysContentTouches = false
}

Примечание для iOS7: вам может потребоваться отключить задержки в UITableViewCell. (Проверьте ответ Димы).

Вы также можете найти другие советы здесь.

Ответ 3

Я решу эту проблему в своем коде, подклассифицируя UIButton,

Цель C

@interface TBButton : UIButton

@end

@implementation TBButton

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    self.highlighted = true;
    [super touchesBegan:touches withEvent:event];
}

- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    self.highlighted = false;
    [super touchesEnded:touches withEvent:event];
}

- (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    self.highlighted = false;
    [super touchesCancelled:touches withEvent:event];
}

@end

быстры

class TBButton: UIButton {
    override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
        highlighted = true
        super.touchesBegan(touches, withEvent: event)
    }

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

swift 3

class TBButton: UIButton {
    override func touchesBegan(_ touches: Set<UITouch>, with event:     UIEvent?) {
        isHighlighted = true
        super.touchesBegan(touches, with: event)
    }

    override func touchesEnded(_ touches: Set<UITouch>, with event:     UIEvent?) {
        isHighlighted = false
        super.touchesEnded(touches, with: event)
    }

    override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
        isHighlighted = false
        super.touchesCancelled(touches, with: event)
    }
}

Ответ 4

версия Swift 3 Alex answer:

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    isHighlighted = true
    super.touchesBegan(touches, with: event)
}

override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
    isHighlighted = false
    super.touchesEnded(touches, with: event)
}

override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
    isHighlighted = false
    super.touchesCancelled(touches, with: event)
}

Ответ 5

Здесь рекурсивное решение, которое вы можете добавить в контроллер вида:

+ (void)disableDelayedTouches:(UIView*)view
{
    for(UIView *currentView in view.subviews) {
        if([currentView isKindOfClass:[UIScrollView class]]) {
            ((UIScrollView *)currentView).delaysContentTouches = NO;
        }
        [self disableDelayedTouches:currentView];
    }
}

- (void)viewDidLoad
{
    [super viewDidLoad];
    [self.class disableDelayedTouches:self.view];
}

Ответ 6

Итак, почему задержка для "Touch Down" или UIControlStateHighlighted? Я пытаюсь предоставить мгновенную обратную связь с пользователем, чтобы показать, что кнопка нажата.

Разве Apple еще не объяснила это?

Без этой задержки:

  • Если вы хотите прокрутить представление, и ваш палец коснется чего-либо яркого (например, любой ячейки таблицы с стилем выбора по умолчанию), вы получите эти мигающие артефакты:

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

    Действительно, они не знают о touchDown, собираетесь ли вы нажимать или прокручивать.

  • Если вы хотите прокрутить представление, а ваш палец касается UIButton/UITextField, вид не прокручивается вообще! Button/TextField по умолчанию "сильнее", чем scrollview, он продолжает ждать события touchUpInside.

Ответ 7

Возможно, этот ответ более прост. просто добавьте к нему задержку анимации. как показано ниже:

    [self.publishButton bk_addEventHandler:^(id sender) {
    @strongify(self);
    // reset to normal state with delay
    [UIView animateWithDuration:0.1 animations:^{
        self.publishButton.backgroundColor = [UIColor whiteColor];
    }];
    } forControlEvents:UIControlEventTouchUpInside];

   [self.publishButton bk_addEventHandler:^(id sender) {
    //Set your Image or color
       self.publishButton.backgroundColor = [UIColor redColor];
   } forControlEvents:UIControlEventTouchDown];