下面的代码表示我的问题:(这是自包含在您可以创建一个Xcode项目与空的模板,替换main.m文件中的内容,删除AppDelegate.h / .m文件,并建立它)
//
// 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]));
}
}
基本上我创建一个使用收集意见的跳板式UI。 我UICollectionViewCell子类( 小区 )有一个按钮,就在于部分细胞的内容查看 (即它的父的)边界之外。
的问题是,点击按钮的任何部分的内容查看范围之外(基本上按钮的3/4)不调用按钮操作。 仅在重叠内容查看按钮的点击部分时是按钮的所谓操作方法。
我甚至重写-pointInside:withEvent:
方法在细胞中 ,从而在按钮的氛围将得到确认。 但是,这并不能与点击按钮的问题有帮助。
我猜它可能是做的CollectionView如何处理触摸,但我不知道是什么。 我知道UICollectionView是一个UIScrollView子类,其实我已经测试了覆盖-pointInside:withEvent:
对视图(由子视图滚动视图)含有部分重叠的按钮,解决了点击按钮的问题,但它并没有在这里工作。
任何帮助吗?
**补充:为了记录在案,我目前问题的解决涉及insetting一个较小的子视图内容查看这给细胞它的外观。 删除按钮被添加到内容查看使得其实际上矩形在于内容查看的范围内,但仅部分重叠的小区的可见部分(即,子视图插图)。 所以,我已经得到了我想要的效果,按钮是否工作正常。 但我还是好奇与上述原来实行的问题。
Answer 1:
这个问题似乎与则hitTest / pointInside。 我猜将小区从pointInside如果触摸是按钮是在细胞外,从而按钮不会被打到测试的一部分返回NO。 为了解决这个问题,你必须重写pointInside您UICollectionViewCell子走按钮进去。 您还需要重写则hitTest返回按钮,如果是触摸按钮内部。 下面是示例实现假设你的按钮在叫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预期点是你必须记住调用按钮这些方法之前将点转换接收器的坐标空间。
Answer 2:
在Interface Builder中你已经设置了对象UICollectionViewCell? 由于错误地有一次我设置一个UIView,并分配给它的正确UICollectionViewCell类......但这样的事情后(按钮,标签,ECC)TOR的内容查看不会添加,使他们不因为他们会作何反应?
所以,提醒在IB绘制界面时采取的UICollectionViewCell对象:)
Answer 3:
斯威夫特版本:
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)
}
就是这样......所有子视图
Answer 4:
我成功接收触摸创建作为子类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;
}
我意识到,在故事板添加按钮没有工作,不知道这是固定在最新的Xcode后添加代码按钮。
希望帮助。
Answer 5:
作为公认的答案的要求,我们应该做一个hitTest
为了收到在细胞内接触。 下面是点击测试雨燕4码:
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
for i in (0..<subviews.count-1).reversed() {
let newPoint = subviews[i].convert(point, from: self)
if let view = subviews[i].hitTest(newPoint, with: event) {
return view
}
}
return super.hitTest(point, with: event)
}
Answer 6:
我有一个类似的问题,试图把一个uicollectionview小区的边界之外的删除按钮,并没有缝回应挖掘事件。
我解决它的方法是放置一个UITapGestureRecognizer的集合,并在自来水happend瓶坯下面的代码
//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];
}
Answer 7:
我看到不完全迅速转换原来的答案两个迅速转换。 所以,我只是想给Swift 4
转换原来的答案,以便大家谁愿意可以使用它的。 您可以将代码只是粘贴到你的subclassed
UICollectionViewCell
。 只要确保你改变closeButton
用自己的按钮。
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
var view = closeButton.hitTest(closeButton.convert(point, from: self), with: event)
if view == nil {
view = super.hitTest(point, with: event)
}
return view
}
override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
if super.point(inside: point, with: event) {
return true
}
return !closeButton.isHidden && closeButton.point(inside: closeButton.convert(point, from: self), with: event)
}
Answer 8:
Honus这里有最好的答案在我看来。 事实上只为我工作,所以我一直在回答其他类似的问题,并把他们这样的一个:
我花了几个小时网上淘了一个解决方案,我的UIButton的内线UICollectionView不工作。 我发疯,直到我终于发现,我工作的解决方案。 而且我相信这也是去的正确方法:黑客攻击命中测试。 这是一个可以去很多更深层次的(双关语意),比固定UICollectionView按钮的问题为好,因为它可以帮助您获得点击事件下被阻止来自打通你的其他事件的看法埋任意键的解决方案:
UIButton的在收集观察室没有收到润色内部事件
既然有这么答案是目标C,我也跟着从那里的线索找到迅速解决:
http://khanlou.com/2018/09/hacking-hit-tests/
-
当我将在该单元格,或任何其他五花八门的答案我试过的禁用用户交互,毫无效果。
我上面张贴的解决方案的好处是,你可以留下你的addTarget的和选择功能,你如何被用来做他们,因为他们是最LIKEY从来都不是问题。 你只需要覆盖一个功能,以帮助触摸事件使它到它的目的地。
为什么解决方案的工作原理:
对于前几个小时,我想通手势没有被我的addTarget调用正确注册。 事实证明,各项指标均登记罚款。 触摸事件根本不会达到我的按钮。
现实似乎是从任意数量的SO帖子和文章我看了,那UICollectionView细胞注定要容纳一个动作,而不是多个用于各种原因。 所以,你只应该是使用内置的选择行为。 考虑到这一点,我相信解决这一限制的正确方法是不是破解UICollectionView禁用滚动或用户交互的某些方面。 UICollectionView只是做自己的工作。 正确的方法是破解命中测试拦截水龙头它得到UICollectionView,并找出他们攻上项目之前。 然后你只需发送一个触摸事件,他们攻上的按钮,让你的正常的东西做的工作。
我(从khanlou.com文章)的最终解决方案是把我的addTarget声明,我的选择功能,无论我喜欢(在细胞类或cellForItemAt覆盖),并在单元格类中重写则hitTest功能。
在我的手机I类有:
@objc func didTapMyButton(sender:UIButton!) {
print("Tapped it!")
}
和
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
guard isUserInteractionEnabled else { return nil }
guard !isHidden else { return nil }
guard alpha >= 0.01 else { return nil }
guard self.point(inside: point, with: event) else { return nil }
// add one of these blocks for each button in our collection view cell we want to actually work
if self.myButton.point(inside: convert(point, to: myButton), with: event) {
return self.myButton
}
return super.hitTest(point, with: event)
}
而在我的手机类初始化我有:
self.myButton.addTarget(self, action: #selector(didTapMyButton), for: .touchUpInside)
文章来源: UIButton in cell in collection view not receiving touch up inside event