集合视图中单元格中的 UIButton 未在事件内部接收修饰 [英] UIButton in cell in collection view not receiving touch up inside event

查看:16
本文介绍了集合视图中单元格中的 UIButton 未在事件内部接收修饰的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

以下代码表达了我的问题:(它是自包含的,您可以使用空模板创建 Xcode 项目,替换 main.m 文件的内容,删除 AppDelegate.h/.m 文件并构建它)

The following code expresses my problem: (It's self-contained in that you could create a Xcode project with an empty template, replace the contents of the main.m file, delete the AppDelegate.h/.m files and build it)

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

基本上,我正在使用集合视图创建一个 Springboard 类型的 UI.我的 UICollectionViewCell 子类 (Cell) 有一个按钮,该按钮部分位于单元格的 contentView(即其父视图)边界之外.

Basically I'm creating a Springboard-type UI using collection views. My UICollectionViewCell subclass (Cell) has a button which lies partially outside the cell's contentView (i.e. its superview's) bounds.

问题在于,单击 contentView 边界之外的按钮的任何部分(基本上是按钮的 3/4)都不会调用按钮操作.仅当单击与 contentView 重叠的按钮部分时,才会调用该按钮的操作方法.

The problem is that clicking on any part of the button outside of the contentView bounds (basically 3/4th of the button) doesn't invoke the button action. Only when clicking on the portion of the button that overlaps the contentView is the button's action method called.

我什至重写了 Cell 中的 -pointInside:withEvent: 方法,以便确认按钮中的触摸.但这对按钮点击问题没有帮助.

I've even overridden -pointInside:withEvent: method in Cell so that touches in the button will be acknowledged. But that hasn't helped with the button clicking problem.

我猜这可能与 collectionView 处理触摸的方式有关,但我不知道是什么.我知道 UICollectionView 是一个 UIScrollView 子类,我实际上已经在视图上测试了覆盖 -pointInside:withEvent: (将子视图设为滚动视图)包含部分重叠的按钮解决了按钮单击问题,但在这里不起作用.

I'm guessing it might be something to do with how collectionView handles touches, but I don't know what. I know that UICollectionView is a UIScrollView subclass and I've actually tested that overriding -pointInside:withEvent: on a view (made subview to a scroll view) containing a partially overlapping button solves the button clicking problem, but it hasn't worked here.

有什么帮助吗?

** 添加:作为记录,我目前对该问题的解决方案包括在 contentView 中插入一个较小的子视图,从而使单元格具有外观.删除按钮被添加到 contentView 中,因此它的矩形实际上位于 contentView 的范围内,但仅部分重叠单元格的可见部分(即插入子视图).所以我得到了我想要的效果,并且按钮工作正常.但是我还是很好奇上面原来实现的问题.

** Added: For the record, my current solution to the problem involves insetting a smaller subview to contentView which gives the cell its appearance. The delete button is added to the contentView such that its rect actually lies within the bounds of contentView but only partially overlaps the visible part of the cell (i.e. the inset subview). So I've got the effect I wanted, and the button is working properly. But I'm still curious about the problem with the original implementation above.

推荐答案

问题似乎出在 hitTest/pointInside 上.我猜如果触摸是在单元格外部的按钮部分上,则单元格会从 pointInside 返回 NO ,因此该按钮未经过命中测试.要解决此问题,您必须在 UICollectionViewCell 子类上覆盖 pointInside 以将按钮考虑在内.如果触摸在按钮内部,您还需要覆盖 hitTest 以返回按钮.下面是示例实现,假设您的按钮位于 UICollectionViewCell 子类中名为 deleteButton 的属性中.

The problem appears to be with hitTest/pointInside. I'm guessing the cell is returning NO from pointInside if the touch is on the part of the button that is outside the cell and thus the button doesn't get hit tested. To fix this you have to override pointInside on your UICollectionViewCell subclass to take the button into account. You also need to override hitTest to return the button if the touch is inside the button. Here are example implementations assuming your button is in a property in the UICollectionViewCell subclass called 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];
}

请注意,由于 hitTest 和 pointInside 期望该点位于接收器的坐标空间中,因此您必须记住在调用按钮上的这些方法之前转换该点.

Note that because hitTest and pointInside expect the point to be in the coordinate space of the receiver you have to remember to convert the point before calling those methods on the button.

这篇关于集合视图中单元格中的 UIButton 未在事件内部接收修饰的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆