UIButton не показывает подсветку при нажатии в iOS7
Я просмотрел тонну постов о похожих вещах, но ни один из них не совпал и не решил эту проблему. Начиная с iOS 7, всякий раз, когда я добавляю UIButton
к UITableViewCell
или даже для нижнего колонтитула он работает "отлично", то есть получает целевое действие, но не показывает небольшую подсветку, которая обычно происходит при нажатии на 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;
17 ответов
В этом табличном представлении вы просто добавляете это свойство.
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;
}
}
Начиная с iOS 8 нам нужно применять ту же технику к подпредставлениям UITableView (таблица содержит скрытое представление прокрутки UITableViewWrapperView). Больше нет необходимости повторять подпредставления UITableViewCell.
for (UIView *currentView in tableView.subviews) {
if ([currentView isKindOfClass:[UIScrollView class]]) {
((UIScrollView *)currentView).delaysContentTouches = NO;
break;
}
}
Этот ответ должен быть связан с этим вопросом.
Я пытался добавить это к принятому ответу, но он так и не прошел. Это гораздо более безопасный способ отключения свойства delaysContentTouches для ячеек, поскольку он не ищет определенный класс, а скорее всего, что отвечает на селектор.
В клетке:
for (id obj in self.subviews) {
if ([obj respondsToSelector:@selector(setDelaysContentTouches:)]) {
[obj setDelaysContentTouches:NO];
}
}
В TableView:
self.tableView.delaysContentTouches = NO;
Для решения, которое работает как в iOS7, так и в iOS8, создайте UITableView
подкласс и обычай UITableViewCell
подкласс.
Используйте этот образец UITableView
"s initWithFrame:
- (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;
}
Используйте этот образец UITableViewCell
"s initWithStyle: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;
}
Для решения этой проблемы я создал категорию 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
не принужден
Вот ответ Романа Б в 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;
}
}
}
У меня были подобные проблемы с текстовой UIButton в UITableViewCell, не выделяющейся при прикосновении. То, что исправило это для меня, изменило buttonType с Custom обратно на System.
Установка delaysContentTouches в NO сделала трюк для UIButton только для изображения в том же UITableViewCell.
self.tableView.delaysContentTouches = NO;
Это быстрая версия ответа Рафаэля Пинто выше. Не забудьте его тоже отозвать:)
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, только для 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://stackru.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 в ячейке, теперь выделяются мгновенно.
Слегка измененная версия ответа Криса Харрисона. 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 }
}
}
}
Принятый ответ не сработал для меня.
Наконец, я добавляю нижний код в категорию 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];
}
Решение в 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(кодер:) будет работать. Пожалуйста, поместите точку отладки в ваши функции инициализации, чтобы узнать, какая функция инициализации будет выполняться, затем используйте приведенный выше код, чтобы заставить ее работать. Надеюсь помочь кому-нибудь.
Это решение для меня не работает, я исправил создание подклассов 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;
}
Поскольку 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;
}
}
Возможно, вам также придется отключить "delaysContentTouches" в вашей раскадровке или перо, выбрав таблицу внутри вашего viewController. Кстати, это может не сработать на ios 7, если вы используете tableView внутри viewController, по крайней мере, я не смог заставить его работать.
В Swift 3 это расширение UIView можно использовать на UITableViewCell. Предпочтительно в cellForRowAt
метод.
func removeTouchDelayForSubviews() {
for subview in subviews {
if let scrollView = subview as? UIScrollView {
scrollView.delaysContentTouches = false
} else {
subview.removeTouchDelayForSubviews()
}
}
}
Я написал расширение категории на UITableViewCell
чтобы сделать этот вопрос простым для решения. Это в основном то же самое, что и принятый ответ, за исключением того, что я поднимаюсь вверх по иерархии представления (в отличие от вниз) от UITableViewCell contentView
,
Я рассмотрел полностью "автоматическое" решение, которое сделало бы все клетки добавленными к UITableView
установить их delaysContentTouches
состояние, чтобы соответствовать владению UITableView
"s 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