Botón UIB en celda en la vista de colección que no recibe el retoque dentro del evento

El siguiente código expresa mi problema: (Es independiente porque puede crear un proyecto Xcode con una plantilla vacía, replace el contenido del file main.m, eliminar los files AppDelegate.h / .m y crearlo)

// // main.m // CollectionViewProblem // #import <UIKit/UIKit.h> @interface Cell : UICollectionViewCell @property (nonatomic, strong) UIButton *button; @property (nonatomic, strong) UILabel *label; @end @implementation Cell - (id)initWithFrame:(CGRect)frame { if (self = [super initWithFrame:frame]) { self.label = [[UILabel alloc] initWithFrame:self.bounds]; self.label.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; self.label.backgroundColor = [UIColor greenColor]; self.label.textAlignment = NSTextAlignmentCenter; self.button = [UIButton buttonWithType:UIButtonTypeInfoLight]; self.button.frame = CGRectMake(-frame.size.width/4, -frame.size.width/4, frame.size.width/2, frame.size.width/2); self.button.backgroundColor = [UIColor networkingColor]; [self.button addTarget:self action:@selector(buttonClicked:) forControlEvents:UIControlEventTouchUpInside]; [self.contentView addSubview:self.label]; [self.contentView addSubview:self.button]; } return self; } // Overriding this because the button's rect is partially outside the parent-view's bounds: - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event { if ([super pointInside:point withEvent:event]) { NSLog(@"inside cell"); return YES; } if ([self.button pointInside:[self convertPoint:point toView:self.button] withEvent:nil]) { NSLog(@"inside button"); return YES; } return NO; } - (void)buttonClicked:(UIButton *)sender { NSLog(@"button clicked!"); } @end @interface ViewController : UICollectionViewController @end @implementation ViewController // (1a) viewdidLoad: - (void)viewDidLoad { [super viewDidLoad]; [self.collectionView registerClass:[Cell class] forCellWithReuseIdentifier:@"ID"]; } // collection view data source methods //////////////////////////////////// - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section { return 100; } - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { Cell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"ID" forIndexPath:indexPath]; cell.label.text = [NSString stringWithFormat:@"%d", indexPath.row]; return cell; } /////////////////////////////////////////////////////////////////////////// // collection view delegate methods //////////////////////////////////////// - (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath { NSLog(@"cell #%d was selected", indexPath.row); } //////////////////////////////////////////////////////////////////////////// @end @interface AppDelegate : UIResponder <UIApplicationDelegate> @property (strong, nonatomic) UIWindow *window; @end @implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init]; ViewController *vc = [[ViewController alloc] initWithCollectionViewLayout:layout]; layout.itemSize = CGSizeMake(128, 128); layout.minimumInteritemSpacing = 64; layout.minimumLineSpacing = 64; layout.scrollDirection = UICollectionViewScrollDirectionHorizontal; layout.sectionInset = UIEdgeInsetsMake(32, 32, 32, 32); self.window.rootViewController = vc; self.window.backgroundColor = [UIColor whiteColor]; [self.window makeKeyAndVisible]; return YES; } @end int main(int argc, char *argv[]) { @autoreleasepool { return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); } } 

Básicamente estoy creando una interfaz de usuario tipo Springboard usando vistas de colección. Mi subclass UICollectionViewCell ( Celda ) tiene un button que se encuentra parcialmente fuera de los límites de contenido de la celda (es decir, de la supervisión).

El problema es que hacer clic en cualquier parte del button fuera de los límites de ContentView (básicamente 3/4 del button) no invoca la acción del button. Solo cuando se hace clic en la parte del button que se superpone a contentView se llama al método de acción del button.

Incluso he anulado -pointInside:withEvent: method en Cell para que se -pointInside:withEvent: en count los toques en el button. Pero eso no ha ayudado con el button de clic en el problema.

Supongo que podría tener algo que ver con cómo los manejadores de collectionView tocan, pero no sé qué. Sé que UICollectionView es una subclass UIScrollView y realmente he probado esa invalidation -pointInside:withEvent: en una vista (hecha subvista a una vista de desplazamiento) que contiene un button parcialmente superpuesto resuelve el button que hace clic en el problema, pero no ha funcionado aquí .

¿Alguna ayuda?

** Agregado: para que conste, mi solución actual al problema implica insert una subvista más pequeña para contentView que da a la célula su apariencia. El button de eliminación se agrega a la ContentView de tal manera que su rectángulo se encuentra realmente dentro de los límites de contentView, pero solo se superpone parcialmente a la parte visible de la celda (es decir, la subvista de inserción). Así que tengo el efecto que quería, y el button funciona correctamente. Pero todavía tengo curiosidad sobre el problema con la implementación original anterior.

El problema parece ser con hitTest / pointInside. Supongo que la celda está devolviendo NO desde el punto interior si el toque es por parte del button que está fuera de la celda y, por lo tanto, el button no se analiza. Para solucionarlo, debe anular pointInside en su subclass UICollectionViewCell para tener en count el button. También debe anular el hitTest para devolver el button si el toque está dentro del button. Aquí hay ejemplos de implementaciones suponiendo que su button está en una propiedad en la subclass UICollectionViewCell llamada deleteButton.

 -(UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event { UIView *view = [self.deleteButton hitTest:[self.deleteButton convertPoint:point fromView:self] withEvent:event]; if (view == nil) { view = [super hitTest:point withEvent:event]; } return view; } -(BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event { if ([super pointInside:point withEvent:event]) { return YES; } //Check to see if it is within the delete button return !self.deleteButton.hidden && [self.deleteButton pointInside:[self.deleteButton convertPoint:point fromView:self] withEvent:event]; } 

Tenga en count que debido a que hitTest y pointInside esperan que el punto esté en el espacio de coorderadas del receptor, recuerde convertir el punto antes de llamar a esos methods en el button.

En Interface Builder, ¿ha configurado el object como UICollectionViewCell? Porque erróneamente una vez configuré una UIView y después le asigné la class UICollectionViewCell correcta … pero haciendo esto (botones, tags, etc.) no se agregan a la ContentView para que no respondan como lo harían …

Por lo tanto, recuerde en IB que tome el object UICollectionViewCell al dibujar la interfaz 🙂

Estoy recibiendo con éxito toques a un button creado de la siguiente manera en el file subclasificado UICollectionViewCell.m;

 - (id)initWithCoder:(NSCoder *)aDecoder { self = [super initWithCoder:aDecoder]; if (self) { // Create button UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom]; button.frame = CGRectMake(0, 0, 100, 100); // position in the parent view and set the size of the button [button setTitle:@"Title" forState:UIControlStateNormal]; [button setImage:[UIImage imageNamed:@"animage.png"] forState:UIControlStateNormal]; [button addTarget:self action:@selector(button:) forControlEvents:UIControlEventTouchUpInside]; // add to contentView [self.contentView addSubview:button]; } return self; } 

Agregué el button en el código después de darme count de que los botones agregados en Storyboard no funcionaban, no estoy seguro si esto se arregla en el último Xcode.

Espero que ayude.

Versión Swift:

 override func hitTest(point: CGPoint, withEvent event: UIEvent?) -> UIView? { //From higher z- order to lower except base view; for (var i = subviews.count-2; i >= 0 ; i--){ let newPoint = subviews[i].convertPoint(point, fromView: self) let view = subviews[i].hitTest(newPoint, withEvent: event) if view != nil{ return view } } return super.hitTest(point, withEvent: event) } 

eso es todo … para todas las subViews

Tuve un problema similar al tratar de colocar un button de eliminación fuera de los límites de una celda de uicollectionview y no coincidía para responder a los events de toque.

la forma en que lo resolví fue colocar un UITapGestureRecognizer en la colección y cuando un grifo pasó a formar el siguiente código

 //this works also on taps outside the cell bouns, im guessing by getting the closest cell to the point of click. NSIndexPath* tappedCellPath = [self.collectionView indexPathForItemAtPoint:[tapRecognizer locationInView:self.collectionView]]; if(tappedCellPath) { UICollectionViewCell *tappedCell = [self.collectionView cellForItemAtIndexPath:tappedCellPath]; CGPoint tapInCellPoint = [tapRecognizer locationInView:tappedCell]; //if the tap was outside of the cell bounds then its in negative values and it means the delete button was tapped if (tapInCellPoint.x < 0) [self deleteCell:tappedCell]; }