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

查看:77
本文介绍了集合视图中的单元格中的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.

我甚至覆盖了 -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 ,使得其rect实际上位于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以返回按钮。下面是假设您的按钮位于名为deleteButton的UICollectionViewCell子类中的属性的示例实现。

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天全站免登陆