iOS:TapGestureRecognizer 问题

Posted

技术标签:

【中文标题】iOS:TapGestureRecognizer 问题【英文标题】:iOS: TapGestureRecognizer Issues 【发布时间】:2012-04-10 23:21:13 【问题描述】:

所以我有一个行为类似于照片库的应用程序,并且我正在实现用户删除图像的功能。这是设置:我有 9 个 UIImageView、imageView、imageView2 等。我还有一个“编辑”按钮和一个 tapGesture 操作方法。我将一个 Tap Gesture Recognizer 拖到我在 IB 中的视图上,并将其附加到每个 UIImageViews 上。我还将 tapGesture 操作方法附加到每个 UIImageViews。理想情况下,我希望该方法仅在按下“编辑”按钮时才变为活动状态。当用户点击编辑,然后点击他们要删除的图片时,我希望出现一个 UIAlertView,询问他们是否确定要删除它。这是我正在使用的代码:

- (IBAction)editButtonPressed:(id)sender 
    editButton.hidden = YES;
    backToGalleryButton.hidden = NO;
    tapToDeleteLabel.hidden = NO;


- (IBAction)tapGesture:(UITapGestureRecognizer*)gesture


    UIAlertView *deleteAlertView = [[UIAlertView alloc] initWithTitle:@"Delete"
                                                              message:@"Are you sure you want to delete this photo?"
                                                             delegate:self
                                                    cancelButtonTitle:@"No"
                                                    otherButtonTitles:@"Yes", nil];
    [deleteAlertView show];

    if (buttonIndex != [alertView cancelButtonIndex]) 

        UIImageView *view = [self.UIImageView];
        if (view) 
            [self.array removeObject:view];
        


        CGPoint tapLocation = [gesture locationInView: self.view];
        for (UIImageView *imageView in self.view.subviews) 
            if (CGRectContainsPoint(self.UIImageView.frame, tapLocation)) 
                ((UIImageView *)[self.view]).image =nil;
 


        [self.user setObject:self.array forKey:@"images"];


这段代码显然充满了错误: 此行上的“使用未声明的标识符按钮索引”:if (buttonIndex != [alertView cancelButtonIndex])

“在 PhotoViewController 类型的对象上找不到属性 UIImageView”UIImageView *view = [self.UIImageView];

以及这一行上的“预期标识符”((UIImageView *)[self.view]).image =nil;

我对编程很陌生,我很惊讶我能做到这一点。所以,我只是想弄清楚我需要如何编辑我的代码以使错误消失,并且只要点击 9 个图像视图之一就可以使用它,并且只有在首先按下编辑按钮。我之前使用标签,效果很好,但是我通过 NSData 保存图像,所以我不能再使用标签了。非常感谢任何帮助,谢谢!

【问题讨论】:

【参考方案1】:

首先,您不想将点击手势附加到图像视图。此外,如果您将拥有超过 9 个图像,您可能需要滚动视图,或单独处理滚动。首先,移除该手势识别器及其所有连接。

接下来,确定您将用作画廊画布的视图类型。一个简单的视图,或者一个滚动视图,或者其他什么......现在并不重要,只是为了让它工作。您想将该视图按住 ctrl 并拖动到您的界面定义中,因此它会为该视图放置一个 IBOutlet(这样您就可以在代码中引用它)。

你将把你的 ImageViews 放到我刚才提到的视图上。

你可以有一个手势识别器的内部标志,但它也有一个属性,可以在你想要的时候启用/禁用它。因此,您可以相当轻松地使其处于活动/非活动状态。

您要做的就是将一个轻击手势识别器放到您的控制器上,并将其连接到控制器的实现部分。它将生成一个用于处理识别器的存根。它将为控制器“一般”解释点击,并在视图上进行点击时调用您的代码。

更多代码...

为滚动视图中的“新”图像创建一个框架。

- (CGRect)frameForData:(MyData*)data atIndex:(NSUInteger)idx

    CGPoint topLeft;
    int row = idx / 4;
    int col = idx % 4;
    topLeft.x = (col+1)*HORIZ_SPACING + THUMBNAIL_WIDTH * (col);
    topLeft.y = (row+1)*VERT_SPACING + THUMBNAIL_HEIGHT * (row);
    return CGRectMake(topLeft.x, topLeft.y, THUMBNAIL_WIDTH, THUMBNAIL_HEIGHT);

为每条元数据创建一个图像视图和一个小边框。

- (UIImageView*)createImageViewFor:(MetaData*)metadata

    UIImageView *imageView = [[UIImageView alloc] initWithImage:metadata.lastImage];
    imageView.frame = CGRectMake(0, 0, THUMBNAIL_WIDTH, THUMBNAIL_HEIGHT);;
    imageView.layer.borderColor = [[UIColor blackColor] CGColor];
    imageView.layer.borderWidth = 2.0;
    imageView.userInteractionEnabled = YES;
    return imageView;

这是创建视图并将其添加到父级的地方...

    imageView = [self createImageViewFor:metadata];
    //[data.imageView sizeToFit];

    // Make sure the scrollView contentSize represents the data
    CGRect lastFrame = [self frameForData:data atIndex:self.data.count-1];
    CGFloat newHeight = lastFrame.origin.y + lastFrame.size.height;
    if (self.bookshelfScrollView.contentSize.height < newHeight) 
        CGSize newSize = self.bookshelfScrollView.contentSize;
        newSize.height = newHeight;
        self.bookshelfScrollView.contentSize = newSize;
    
    [self.bookshelfScrollView addSubview:data.imageView];

因此,您只需创建每个框架,将它们添加到视图中,您唯一需要做的就是在它们上启用用户交互,否则滚动视图不允许手势通过。

好的......看看你发布的代码......因为你没有说它有什么问题......很难说......下面是你的代码......我的cmets是,嗯,评论...

- (IBAction)editButtonPressed:(id)sender 
    editButton.hidden = YES;
    backToGalleryButton.hidden = NO;
    tapToDeleteLabel.hidden = NO;

- (IBAction)tapGesture:(UITapGestureRecognizer*)gesture

    // I don't think I'd do this here, but it shouldn't cause "problems."
    UIAlertView *deleteAlertView = [[UIAlertView alloc] initWithTitle:@"Delete"
                                                              message:@"Are you sure you want to delete this photo?"
                                                             delegate:self
                                                    cancelButtonTitle:@"No"
                                                    otherButtonTitles:@"Yes", nil];
    [deleteAlertView show];

    // In your controller, you have the main view, which is the view
    // on which you added your UIViews.  You need that view.  Add it as an IBOutlet
    // You should know how to do that...  ctrl-drag to the class INTERFACE source.
    // Assuming you name it "galleryView"

    // Take the tap location from the gesture, and make sure it is in the
    // coordinate space of the view.  Loop through all the imageViews and
    // find the one that contains the point where the finger was taped.
    // Then, "remove" that one from its superview...
    CGPoint tapLocation = [gesture locationInView: self.galleryView];
    for (UIImageView *imageView in self.galleryView.subviews) 
        if (CGRectContainsPoint(imageView.frame, tapLocation)) 
            [imageView removeFromSuperview];
        
    

【讨论】:

嗯,有道理,非常感谢!所以,我删除了我所做的一切,然后我将一个新的点击手势识别器拖到我的控件上(它最终出现在 IB 的侧栏中)。然后,我从点击手势识别器控制拖到控制图标上,出现的出口是“代表”。我也连接了它。我做了正确的动作吗?很抱歉提出令人难以置信的新手问题,非常感谢您的帮助。 没有。我确定您已将 ctrl 从按钮拖到您的代码中,对吗?它会生成一个您填写的函数骨架,并在按下按钮时调用该函数。用轻击手势做同样的事情。按住 Ctrl 键将其拖动到您的代码中,它会连接一个函数,当用户点击屏幕时会调用该函数。这就是代码找到合适的 UIImageView 的地方。 我实际上是从左侧边栏上的点击手势识别器图标拖动到它正上方的控制图标。抱歉,我不明白,如何控制从点击手势识别器图标拖动到我的实际代码?我应该控制拖动到视图控制器的 .m 文件吗? 是的!从手势拖动到源文件 (.m)。 非常感谢,我刚刚成功了。你知道我可以如何修复上面代码中的错误吗?我以为我写的都是正确的,一切都会发生。但由于某种原因,它仍然存在一些错误。再次感谢!【参考方案2】:

就个人而言,在我的小照片库中,我绕过了所有手势识别器,将UIImageView 控件替换为UIButton 控件,像设置UIImageView 一样设置按钮的图像属性。它看起来一模一样,但是你可以免费点击缩略图,根本不需要手势。

所以,我的视图只有一个UIScrollView,我以编程方式将我的图像添加为按钮,并为按钮控件设置图像,例如:

- (void)loadImages

    listOfImages = [ImageData newArrayOfImagesForGallery:nil];

    // some variables to control where I put my thumbnails (which happen to be 76 x 76px)

    int const imageWidth = 76;
    int const imageHeight = imageWidth;
    NSInteger imagesPerRow;
    NSInteger imagePadding;
    NSInteger cellWidth;
    NSInteger cellHeight;

    // some variables to keep track of where I am as I load in my images

    int row = 0;
    int column = 0;
    int index = 0;

    // add images to navigation bar

    for (ImageData *item in listOfImages)
    
        // figure out what row and column I'm on

        imagesPerRow = self.view.frame.size.width / (imageWidth + 2);
        imagePadding = (self.view.frame.size.width - imageWidth*imagesPerRow) / (imagesPerRow + 1);
        cellWidth = imageWidth + imagePadding;
        cellHeight = imageHeight + imagePadding;

        // this is how I happen to grab my UIImage ... this will vary by implementation

        UIImage *thumb = [item imageThumbnail];

        // assuming I found it...

        if (thumb)
        
            // create my button and put my thumbnail image in it

            UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
            button.frame = CGRectMake(column * cellWidth  + imagePadding, 
                                      row    * cellHeight + imagePadding, 
                                      imageWidth, 
                                      imageHeight);
            [button setImage:thumb forState:UIControlStateNormal];
            [button addTarget:self
                       action:@selector(buttonClicked:)
             forControlEvents:UIControlEventTouchUpInside];
            button.tag = index++;
            [[button imageView] setContentMode:UIViewContentModeScaleAspectFit];

            // add it to my view

            [scrollView addSubview:button];

            // increment my column (and if necessary row) counters, making my scrollview larger if I need to)

            if (++column == imagesPerRow) 
            
                column = 0;
                row++;
                [scrollView setContentSize:CGSizeMake(self.view.frame.size.width, (row+1) * cellHeight + imagePadding)];
            
        
    


// I also have this in case the user changes orientation, so I'll move my images around if I need to

- (void)rearrangeImages

    if (!listOfImages)
    
        [self loadImages];
        return;
    

    // a few varibles to keep track of where I am

    int const imageWidth = 76;
    int const imagesPerRow = self.view.frame.size.width / (imageWidth + 2);
    int const imageHeight = imageWidth;
    int const imagePadding = (self.view.frame.size.width - imageWidth*imagesPerRow) / (imagesPerRow + 1);
    int const cellWidth = imageWidth + imagePadding;
    int const cellHeight = imageHeight + imagePadding;

    NSArray *buttons = [[NSArray alloc] initWithArray:[scrollView subviews]];

    int row;
    int column;
    int index;

    CGRect newFrame;

    // iterate through the buttons

    for (UIView *button in buttons)
    
        index = [button tag];

        if ([button isKindOfClass:[UIButton class]] && index < [listOfImages count])
        
            // figure out where the button should go

            row = floor(index / imagesPerRow);
            column = index % imagesPerRow;
            newFrame = CGRectMake(column * cellWidth  + imagePadding, 
                                  row    * cellHeight + imagePadding, 
                                  imageWidth, 
                                  imageHeight);

            // if we need to move it, then animation the moving

            if (button.frame.origin.x != newFrame.origin.x || button.frame.origin.y != newFrame.origin.y)
            
                [UIView animateWithDuration:0.33 animations:^
                    [button setFrame:newFrame];
                ];
            
        
    

    NSInteger numberOfRows = floor(([listOfImages count] - 1) / imagesPerRow) + 1;

    [scrollView setContentSize:CGSizeMake(self.view.frame.size.width, numberOfRows * cellHeight + imagePadding)];


- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation

    [self rearrangeImages];

希望这能让您了解如何以编程方式添加UIButton 以充当图库中的图像。我试图即时编辑它,所以如果我在此过程中引入任何错误,我会提前道歉,但它应该让您了解您可以想象到的事情......我删除了我的代码以执行此操作单独的 GCD 队列(我这样做是因为我有接近 100 个图像,而在主队列上这样做太慢了。)

无论如何,您可以创建 buttonClicked 方法来显示您的 UIAlertView

- (void)buttonClicked:(UIButton *)sender

     if (inEditMode)
     
         // create your UIAlterView using [sender tag] to know which image you tapped on
     

最后,您的UIAlertView 上有两个按钮,但您似乎没有检查用户点击了哪个按钮。您的视图控制器应该是 UIAlterViewDelegate,您已在其中定义了您的 alertView:clickedButtonAtIndex,它将执行您的实际编辑步骤。

- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex

     // ok, will do what I need to do with whatever the user tapped in my alertview

【讨论】:

【参考方案3】:

我能想到的最简单的事情是默认禁用手势,然后在点击编辑按钮后启用手势。这样,图片只有在“编辑”模式下才会响应点击手势识别器。

【讨论】:

太棒了,非常感谢,我现在就研究一下。另外,您对如何消除上述错误代码有任何想法吗?我以为一切都会好起来的,但我不知道为什么没有。很抱歉新手问题,我正在学习。

以上是关于iOS:TapGestureRecognizer 问题的主要内容,如果未能解决你的问题,请参考以下文章

带有 TapGestureRecognizer 的最前面的 UIView 没有收到触摸

如何为多个 UILabel 创建一个 tapGestureRecognizer?

禁用同时 rightBarButton / tapGestureRecognizer 触摸

将 TapGestureRecognizer 添加到除 UICollectionView 单元格之外的整个视图

Xamarin.Forms 自定义 TapGestureRecognizer 附加属性

UIToolbarButton 上的 TapGestureRecognizer 不起作用