xcode iOS:TapGestureRecognizer 问题

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/10097855/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-14 23:56:43  来源:igfitidea点击:

iOS: TapGestureRecognizer Issues

objective-ciosxcodeuiimageviewuitapgesturerecognizer

提问by Henry F

So I have an app that behaves like a photo gallery and I'm implementing the ability for the user to delete the images. Here is the setup: I have 9 UIImageViews, imageView, imageView2, etc. I also have an "Edit" button, and a tapGesture action method. I dragged a Tap Gesture Recognizer over onto my view in IB, and attached it to each one of the UIImageViews. I also attached the tapGesture action method to each of the UIImageViews. Ideally, I would like the method to only become active when the "Edit" button is pressed. When the user taps Edit, then taps on the picture they want to delete, I would like a UIAlertView to appear, asking if they are sure they want to delete it. Here is the code I'm using:

所以我有一个像照片库一样的应用程序,我正在实现用户删除图像的能力。这是设置:我有 9 个 UIImageViews、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"];
}
}

This code is obviously riddled with errors: "Use of undeclared identifier button index" on this line: if (buttonIndex != [alertView cancelButtonIndex])

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

"Property UIImageView not found on object of type PhotoViewController" on this line UIImageView *view = [self.UIImageView];

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

And "Expected identifier" on this line ((UIImageView *)[self.view]).image =nil;

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

I'm very new to programming, and I'm surprised that I even made it this far. So, I'm just trying to figure out how I need to edit my code so that the errors go away, and that it can be used whenever one of the 9 image views is tapped, and also so that this method only fires when the Edit button is pushed first. I was using tags earlier, and it worked great, but I save the images via NSData, so I can't use tags anymore. Any help is much appreciated, thanks!

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

回答by Jody Hagins

First, you don't want to attach the tap gesture to the image views. Also, if you are going to have more than 9 images, you may want a scroll view, or handle scrolling separately. First, remove that gesture recognizer and all its connections.

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

Next, determine what type of view you will use as your gallery canvas. A simple View, or a ScrollView, or whatever... it doesn't really matter right now, just to get it working. You want to ctrl-drag that view into your interface definition, so it drops an IBOutlet for the view (that way you can reference it in code).

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

You will place your ImageViews onto the view I just mentioned.

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

You can have an internal flag for the gesture recognizer, but it also has a property that use can enable/disable it whenever you want. Thus, you can have it active/inactive fairly easily.

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

All you want to do is drop a single tap-gesture-recognizer onto your controller, and connect it to the implementation section of the controller. It will generate a stub for handling the recognizer. It will interpret taps "generally" for the controller, and call your code whenever a tap is made on the view.

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

Some more code...

还有一些代码...

Creates a frame for the "new" image in the scroll view.

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

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

Creates an image view for each piece of metadata, and a small border.

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

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

This is where the views are created and added to the parent...

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

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

So, you just create each frame, add them to the view, and the only thing you have to do is enable user interaction on them, because otherwise the scroll view does not allow the gesture through.

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

OK... Looking at the code you posted... since you didn't say what was wrong with it... hard to say... The below is your code... My comments are, well, Comments...

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

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

回答by Rob

Personally, in my little photo gallery, I bypassed all of the gesture recognizer stuff by replacing the UIImageViewcontrols with UIButtoncontrols, setting the image property for the button like you would for the UIImageView. It looks identical, but then you get the functionality of tapping on a thumbnail for free, with no gestures needed at all.

就我个人而言,在我的小照片库中,我通过用UIImageView控件替换控件UIButton来绕过所有手势识别器的东西,像设置UIImageView. 它看起来一模一样,但是你可以免费点击缩略图,完全不需要手势。

So, my view just has a UIScrollView, which which I programmatically add my images as buttons with the image set for the button control, e.g.:

所以,我的视图只有一个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];
}

Hopefully this gives you an idea of how you can programmatically add a UIButtonto act as an image in a gallery. I've tried to edit this on the fly, so I apologize in advance if I introduced any errors in the process, but it should give you a sense of what you could conceivably do ... I removed my code to do this in a separate GCD queue (which I do because I have close to 100 images, and doing this on the main queue is too slow.)

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

Anyway, you can then create your buttonClickedmethod to do your display of your UIAlertView.

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

- (void)buttonClicked:(UIButton *)sender
{
     if (inEditMode)
     {
         // create your UIAlterView using [sender tag] to know which image you tapped on
     }
}

Finally, you have two buttons on your UIAlertView, but you don't seem to check to see what button the user clicked on. Your view controller should be a UIAlterViewDelegate, in which you have defined your alertView:clickedButtonAtIndexwhich will do your actual edit steps.

最后,您的 上有两个按钮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
}

回答by 5StringRyan

The easiest thing I can think of off the top of my head is to have the gesture disabled by default, and then have the gesture enabled once the edit button is hit. This way, the picture will only respond to the tap gesture recognizer if it is in "Edit" mode.

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