iOS:在 UIImageViews 之间移动图像

Posted

技术标签:

【中文标题】iOS:在 UIImageViews 之间移动图像【英文标题】:iOS: Move images between UIImageViews 【发布时间】:2013-02-19 19:37:17 【问题描述】:

我有一个视图控制器,其中有一个默认的 UIview 放置整个屏幕。我在这个视图中添加了几个 UIImageViews(比如 20 个左右)。我想支持在这些 UIImageViews 之间交换图像。我已经使用 UITapGestureRecogniser 来处理这些 UIImageViews 上发生的触摸。我可以在这个 UITapGestureRecogniser 事件本身下处理这些 UIImageViews 之间的图像拖放吗? (或)我需要使用 UIPanGestureRecognizer 或触摸事件吗?在这些 UIImageView 之间实现图像拖放的最简单和最简单的方法是什么?请建议?

谢谢!

【问题讨论】:

【参考方案1】:

一些想法。

    您可以通过点击手势来实现这一点(例如点击第一个图像视图,点击目标图像视图),但这不是很直观。使用UIPanGestureRecognizer 进行适当的拖放操作对您的最终用户来说会更加直观。

    从技术上讲,您不是从图像视图中拖动图像,而是实际上是在拖动图像视图本身。 (如果没有图像视图,图像本身就没有视觉表示。)当您放手时,您将动画化图像视图帧的变化以完成错觉。


如果你有一个imageViews 的 NSArray,你可以在他们的超级视图中添加一个手势:

@property (nonatomic, strong) NSMutableArray *imageViews;

- (void)viewDidLoad

    [super viewDidLoad];

    [self createImageViewArray];

    UIPanGestureRecognizer *gesture = [[UIPanGestureRecognizer alloc] initWithTarget:self
                                                                              action:@selector(handlePan:)];
    [self.view addGestureRecognizer:gesture];


- (void)createImageViewArray

    self.imageViews = [NSMutableArray array];

    // add your imageviews to the array any way you want

    ...


- (void)handlePan:(UIPanGestureRecognizer *)gesture

    static UIImageView *draggedImage = nil;
    static CGRect draggedImageOriginalFrame;

    CGPoint location = [gesture locationInView:gesture.view];

    if (gesture.state == UIGestureRecognizerStateBegan)
    
        // see if we grabbed an imageview

        draggedImage = [self determineImageForLocation:location];

        // if so, save its old location for future reference and bring it to the front

        if (draggedImage)
        
            draggedImageOriginalFrame = draggedImage.frame;
            [draggedImage.superview bringSubviewToFront:draggedImage];
        
    
    else if (gesture.state == UIGestureRecognizerStateChanged && draggedImage != nil)
    
        // if we're dragging it, then update its location 

        CGPoint translation = [gesture translationInView:gesture.view];
        CGRect frame = draggedImageOriginalFrame;
        frame.origin.x += translation.x;
        frame.origin.y += translation.y;
        draggedImage.frame = frame;
    
    else if (draggedImage != nil && (gesture.state == UIGestureRecognizerStateEnded ||
                                     gesture.state == UIGestureRecognizerStateCancelled ||
                                     gesture.state == UIGestureRecognizerStateFailed))
    
        // if we let go, let's see if we dropped it over another image view

        UIImageView *droppedOver = nil;

        if (gesture.state == UIGestureRecognizerStateEnded)
            droppedOver = [self draggedImageView:draggedImage toLocation:location];

        if (droppedOver == nil)
        
            // fail; animate the restoring of the view back to it's original position

            [UIView animateWithDuration:0.25
                             animations:^
                                 draggedImage.frame = draggedImageOriginalFrame;
                             ];
        
        else
        
            // succeed; make sure to bring the view we're about to animate to the front

            [droppedOver.superview bringSubviewToFront:droppedOver];

            // animate the swapping of the views

            [UIView animateWithDuration:0.25
                             animations:^
                                 draggedImage.frame = droppedOver.frame;
                                 droppedOver.frame = draggedImageOriginalFrame;
                             ];
        
    


// see if we dragged an imageview over one of our imageviews, returning a point
// to the image view we dropped it over

- (UIImageView *)draggedImageView:(UIImageView *)draggedView toLocation:(CGPoint)location

    for (UIImageView *imageview in self.imageViews)
        if (CGRectContainsPoint(imageview.frame, location) && imageview != draggedView)
            return imageview;

    return nil;


// see if we started the gesture over an imageview
// (turns out that this is the same as "did we drag an image over another",
// but as we're not dragging an image yet, just pass nil)

- (UIImageView *)determineImageForLocation:(CGPoint)location

    return [self draggedImageView:nil toLocation:location];

【讨论】:

好的,知道了!谢谢。我还有一个疑问是,在这种情况下,我如何将两个图像视图移动(交换)到指定的交换位置。 @Getsy 查看示例代码。请注意,如果您在NSArray 中获取图像视图,此类内容总是会被简化,因此您可以快速枚举它们。【参考方案2】:

Rob 的回答非常好。我稍微修改了他的handlePan 方法以允许图像的热交换。

- (void)handlePan:(UIPanGestureRecognizer *)gesture

    static UIImageView *draggedImage = nil;
    static CGRect draggedImageLastFrame;

    CGPoint location = [gesture locationInView:gesture.view];

    if (gesture.state == UIGestureRecognizerStateBegan) 

        // see if we grabbed an imageview

        draggedImage = [self determineImageForLocation:location];

        // if so, save its old location for future reference and bring it to the front

        if (draggedImage) 
            draggedImageLastFrame = draggedImage.frame;
            [draggedImage.superview bringSubviewToFront:draggedImage];
            draggedImage.alpha = 0.6f;
        

     else if (gesture.state == UIGestureRecognizerStateChanged && draggedImage != nil) 

        // if we're dragging it, then update its horizontal location

        CGPoint translation = [gesture translationInView:gesture.view];
        CGRect frame = draggedImage.frame;
        frame.origin.x += translation.x;
        draggedImage.frame = frame;
        [gesture setTranslation:CGPointZero inView:gesture.view];

        UIImageView *draggedOver = [self draggedImageView:draggedImage toLocation:location];
        if (draggedOver != nil) 

            // animate the draggedOver image to the the draggedImage's last location

            [UIView animateWithDuration:0.25
                             animations:^
                                 CGRect currentFrame = draggedOver.frame;
                                 draggedOver.frame = draggedImageLastFrame;
                                 draggedImageLastFrame = currentFrame;
                             ];
        

     else if (draggedImage != nil && (gesture.state == UIGestureRecognizerStateEnded ||
                                       gesture.state == UIGestureRecognizerStateCancelled ||
                                       gesture.state == UIGestureRecognizerStateFailed)) 

        // finished, animate the draggedImage into place

        [UIView animateWithDuration:0.25
                         animations:^
                             draggedImage.frame = draggedImageLastFrame;
                             draggedImage.alpha = 1.0f;
                         ];
    

【讨论】:

【参考方案3】:

事实上,根本不要使用点击手势。

秘诀就是使用imageView的superview。

您需要 3 个图像视图......

UIImageView *ImageViewA

UIImageView *ImageViewB

UIImageView *DraggableImageView

假设你点击屏幕并且宿主视图得到一个 touchesBegan 事件...如果你拿 ImageViewA 并用下面的方法测试它的 rect:

[rect containsPoint:touchPoint];

如果这是真的并且你已经成功地触摸了第一个 imageView... 在 touchesMoved 中开始拖动操作,如下所示:

如果您使用 ImageViewA.superview,您将获得视图的主机视图...然后主机视图应收到以下命令:

[newDraggableImageView setFrame:ImageViewA.frame];
[newDraggableImageView setImage:ImageViewA.image];
[hostView addSubview:newDraggableImageView];

现在您可以使用 UIResponder 方法 touchesBegain touchesMoved 和 touchesEnded 跟踪宿主视图上的触摸事件

当您通过 touchesMoved:将移动的坐标发送到 newDraggableImageView 的框架中...

因为它正在拖动... touchesEnded 方法之一:

如果图像的框架在第二个拖动点的框架内,那么你可以设置最终的图像视图

if ([frame containsRect:anotherFrame])
    ImageViewB.image = draggableImageView.image;

抱歉,这是伪代码...希望您了解逻辑流程...将图像添加到主机视图...或应用程序的窗口视图并移动它...一旦移动结束...测试对于 imageView 的位置并适当地设置新的图像视图...

祝你好运!快乐编码!

【讨论】:

以上是关于iOS:在 UIImageViews 之间移动图像的主要内容,如果未能解决你的问题,请参考以下文章

iOS:在数组中存储指向 UIImageViews 的链接,然后使用它们

UIStackView不改变宽度

具有多个 UiImageViews 的 SDWebImage 和 UITableViewCell 不显示所有图像

如何使用 UIPanGestureRecognizer 移动 2 个或更多 UIImageViews

结合 UIImageViews 产生 0 字节的空图像

具有相同图像的多个 UIImageViews 给出不一致错误