iOS-Evento "Touch Down" retrasado para UIButton en UITableViewCell
objective-c (7)
Tengo una UITableViewCell personalizada, que se inicializa de la siguiente manera:
- (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;
}
El botón aparece correctamente, con la imagen de fondo apropiada, pero la imagen resaltada no aparece instantáneamente cuando se presiona / se presiona el botón. En cambio, debe mantenerlo presionado durante uno o dos segundos antes de que ocurra el cambio. Al soltar el botón, por otro lado, tiene un cambio instantáneo a la imagen de fondo original.
Tratando de mitigar el cambio tardío cuando cambio a la imagen resaltada, coloco el cambio en el siguiente método:
- (IBAction)downDown:(id)sender {
[self.downButton setBackgroundColor:[UIColor redColor]];
}
El método anterior está configurado para "Touch Down" (opuesto al "Touch Up Inside" más común), y he eliminado setBackgroundImage:forState:
para el estado resaltado. El mismo problema que se mencionó anteriormente. El color finalmente cambia a rojo, pero solo después de hacer clic y mantener presionado el botón durante uno o dos segundos.
Tengo un método para el botón al que se llama cuando se produce "Touch Up Inside", y ese método se ejecuta sin problemas, independientemente de que toque rápidamente el botón o haga clic y sostenga durante un tiempo antes de soltarlo.
Entonces, ¿por qué la demora para "Touch Down" o UIControlStateHighlighted
? Intento proporcionar retroalimentación instantánea al usuario para mostrar que se ha presionado el botón.
Puedo proporcionar más código si es necesario, pero estos son los únicos bits que tienen algo que ver con la apariencia de fondo.
Entonces, ¿por qué la demora para "Touch Down" o
UIControlStateHighlighted
? Intento proporcionar retroalimentación instantánea al usuario para mostrar que se ha presionado el botón.
¿Apple no lo ha explicado todavía?
Sin este retraso:
Si desea desplazar la vista, y su dedo toca algo resaltable (por ejemplo, cualquier celda de tabla con el estilo de selección predeterminado), obtiene estos artefactos parpadeantes:
Realmente, no saben en
touchDown
, ya sea que vayas a tocar o desplazarte.Si desea desplazarse por la vista, y su dedo toca un
UIButton
/UITextField
, ¡la vista no se desplaza en absoluto ! El comportamiento predeterminado de Button / TextField es "más fuerte" que la vista de desplazamiento, sigue esperando un eventotouchUpInside
.
Aquí hay una solución recursiva que puede agregar a su controlador de vista:
+ (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];
}
Esto es causado por la propiedad UIScrollView
delaysContentTouches
.
Solía ser suficiente establecer esa propiedad en NO
para UITableView
, pero eso solo funcionará para las subvistas de la tabla que no estén encerradas en otro UIScrollView
.
UITableViewCells
contiene una vista de desplazamiento interno en iOS 7 por lo que deberá cambiar el valor de esta propiedad en el nivel de celda para todas las celdas con botones en ellas.
Aquí está lo que tú necesitas hacer:
1.en viewDidLoad
o en algún lugar similar una vez que se haya inicializado su UITableView
, ponga esto en:
self.tableView.delaysContentTouches = NO;
2. Para soporte iOS 7, en el método de inicialización para su UITableViewCell
( initWithStyle:reuseIdentifier:
o initWithCoder:
para NIB), ponga esto al final:
for (UIView *currentView in self.subviews)
{
if([currentView isKindOfClass:[UIScrollView class]])
{
((UIScrollView *)currentView).delaysContentTouches = NO;
break;
}
}
Desafortunadamente, esta no es una solución 100% permanente ya que Apple puede cambiar la jerarquía de vista dentro de las celdas nuevamente en el futuro (tal vez moviendo la vista de desplazamiento otra capa hacia abajo o algo que requeriría anidar otro lazo allí), pero hasta que emerjan clase o al menos la propiedad de los desarrolladores de alguna manera, esto es lo mejor que tenemos.
Resuelvo este problema en mi código subclasificando UIButton,
C objetivo
@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
rápido
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)
}
}
veloz 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)
}
}
Solución Swift 3 (para iOS8 +):
En primer lugar, apague los retrasos en UITableView
después de que se cargue correctamente, por ejemplo, dentro del método viewDidLoad()
:
someTableView.delaysContentTouches = false
Luego apague las demoras en las vistas de desplazamiento contenidas dentro de UITableView
:
for case let scrollView as UIScrollView in someTableView.subviews {
scrollView.delaysContentTouches = false
}
Nota para iOS7: es posible que UITableViewCell
deshabilitar los retrasos en UITableViewCell
. (Verifique la respuesta de Dima).
También puede encontrar algunos otros consejos here .
Swift 3 versión de la answer de :
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)
}
Tal vez esta respuesta es más simple. solo agréguele un retraso de animación. como abajo:
[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];