收集视图中的单元格中的UIButton不会触及事件内部

下面的代码expression了我的问题:(它是自包含的,你可以创build一个空模板的Xcode项目,replacemain.m文件的内容,删除AppDelegate.h / .m文件并构build它)

// // 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 redColor]; [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])); } } 

基本上我使用集合视图创build一个Springboardtypes的UI。 我的UICollectionViewCell子类( 单元格 )有一个button,部分位于单元格的contentView (即其超视图)范围之外。

问题是单击contentView边界之外的button的任何部分(基本上是button的3/4)不会调用button操作。 只有在点击与contentView重叠的button部分时,才会调用该button的操作方法。

我甚-pointInside:withEvent:Cell中覆盖了-pointInside:withEvent:方法,这样触摸button就会被确认。 但是,这并没有帮助点击button问题。

我猜这可能是如何与collectionView处理触摸,但我不知道是什么。 我知道UICollectionView是一个UIScrollView的子类,我实际上已经testing-pointInside:withEvent:在一个视图(使子视图滚动视图)包含部分重叠的button解决button单击问题,但它没有在这里工作。

任何帮助?

**补充:为了logging,我目前的解决scheme涉及插入一个较小的子视图contentView ,使单元格的外观。 删除button被添加到contentView ,使得它的rect实际上位于contentView的范围之内,但是只与cell的可见部分(即inset子视图)部分重叠。 所以我得到了我想要的效果,并且button工作正常。 但是我仍然对上面原始实现的问题感到好奇。

问题似乎与hitTest / pointInside。 我猜测单元格是否从pointInside返回NO,如果触摸是在单元格外的button的一部分,因此button不被命中testing。 要解决这个问题,你必须重写你的UICollectionViewCell子类中的pointInside来考虑button。 如果触摸在button内,您还需要重写hitTest以返回button。 假设您的button位于名为deleteButton的UICollectionViewCell子类的属性中,这里是示例实现。

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

请注意,因为hitTest和pointInside期望点在接收器的坐标空间中,所以您必须记住在调用button上的这些方法之前转换点。

在Interface Builder中,您是否已经将对象设置为UICollectionViewCell? 因为错误地一次我设置了一个UIView,并在分配给它正确的UICollectionViewCell类…但做这些事情(button,标签,ecc)不添加到contentView,所以他们不响应,因为他们会…

因此,在提醒IB在绘制界面时采取UICollectionViewCell对象:)

我正在成功地接收到在子类UICollectionViewCell.m文件中创build的button,如下所示:

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

我意识到在Storyboard中添加的button不起作用后,我在代码中添加了button,不知道这是否在最新的Xcode中修复。

希望有所帮助。

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

就是这样…对于所有的子视图

我有一个类似的问题试图放置一个删除button超出了一个uicollectionview单元格的边界,并没有缝隙响应轻击事件。

我解决这个问题的方法是在集合上放置一个UITapGestureRecognizer,当一个轻敲发生的时候预制下面的代码

 //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]; }