UIPanGestureRecognizer 计算帧原点以保持相邻的子视图位置以进行图像裁剪

UIPanGestureRecognizer calculate frame origin to maintain adjacent subview position for image cropping

我有一个 cropView,其中包含一个 cropBoxView 子视图,该子视图还包含四个正方形子视图,每个子视图上都有一个 UIPanGestureRecognizer 以启用裁剪区域的大小调整。

我想做的是改变框架大小但保持相邻方角的位置,这意味着我需要计算一个新的原点。我能够成功更改帧大小,但我不知道如何计算新原点。

目前,如果我平移视图的右下角,它会按我想要的方式工作(无需在下面的代码中调整原点),因为相邻的角是左上角,所以它的原点不会需要改变。

如果能提供任何帮助,我将不胜感激。

编辑:请参阅下面我的答案以获取结果和代码的示例 GIF

CGPoint translation = [recognizer translationInView:self.cropView];

CGRect recognizerFrame = self.cropView.cropBoxView.frame;

// Todo: calculate new origin based on adjacent crop corner

CGFloat testX = recognizerFrame.size.width += translation.x;
CGFloat testY = recognizerFrame.size.height += translation.y;

recognizerFrame.origin.x = recognizerFrame.origin.x - (recognizerFrame.size.width - testX);
recognizerFrame.origin.y = recognizerFrame.origin.y - (recognizerFrame.size.height - testY);

recognizerFrame.size.width += translation.x;
recognizerFrame.size.height += translation.y;

[recognizer setTranslation:CGPointZero inView:self.cropView];

我自己解决了这个问题,它似乎工作得很好。

结果:

我所做的是子类化 UIPanGestureRecognizer 并定义一个枚举,用于调用手势的 shouldReceiveTouch 委托方法以确定在 cropBoxView 中触摸了哪个角。所以现在每个角都有一个单独的 UIPanGestureRecognizer,我现在所有四个角都只有一个。

代码:

CropBoxCornerPanGestureRecognizer.h

#import <UIKit/UIGestureRecognizerSubclass.h>
#import <UIKit/UIPanGestureRecognizer.h>

typedef NS_ENUM(NSUInteger, corner)
{
    TopLeftCorner = 1,
    TopRightCorner,
    BottomLeftCorner,
    BottomRightCorner
};

@interface CropBoxCornerPanGestureRecognizer : UIPanGestureRecognizer

@property (nonatomic, assign) NSUInteger corner;

@end

CropBoxCornerPanGestureRecognizer.m

#import "CropBoxCornerPanGestureRecognizer.h"

@interface CropBoxCornerPanGestureRecognizer ()

@end

@implementation CropBoxCornerPanGestureRecognizer

@end

ViewController.h:

@interface ViewController : UIViewController <UIGestureRecognizerDelegate>

@end

ViewController.m:

@interface ViewController ()

@property (nonatomic, strong) CropBoxCornerPanGestureRecognizer *cropBoxCornerPanRecognizer;

@end

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];

    self.cropBoxCornerPanRecognizer = [[CropBoxCornerPanGestureRecognizer alloc]init];
    self.cropBoxCornerPanRecognizer.maximumNumberOfTouches = 1;
    self.cropBoxCornerPanRecognizer.delaysTouchesBegan = NO;
    self.cropBoxCornerPanRecognizer.delaysTouchesEnded = NO;
    self.cropBoxCornerPanRecognizer.cancelsTouchesInView = NO;
    [self.cropBoxCornerPanRecognizer addTarget:self action:@selector(panCropBoxCorner:)];
    self.cropBoxCornerPanRecognizer.delegate = self;

    self.cropView.cropBoxView addGestureRecognizer:self.cropBoxCornerPanRecognizer];
}

- (void)panCropBoxCorner:(CropBoxCornerPanGestureRecognizer *)recognizer
{   
    if (recognizer.state == UIGestureRecognizerStateBegan || recognizer.state == UIGestureRecognizerStateChanged)
    {
        CGPoint translation = [recognizer translationInView:self.cropView];

        CGRect recognizerFrame = self.cropView.cropBoxView.frame;

        if (recognizer.corner == TopLeftCorner)
        {
            recognizerFrame.size.width -= translation.x;
            recognizerFrame.size.height -= translation.y;

            recognizerFrame.origin.x += translation.x;
            recognizerFrame.origin.y += translation.y;
        }
        else if (recognizer.corner == TopRightCorner)
        {
            recognizerFrame.size.width += translation.x;
            recognizerFrame.size.height -= translation.y;

            recognizerFrame.origin.y += translation.y;
        }
        else if (recognizer.corner == BottomLeftCorner)
        {
            recognizerFrame.size.width -= translation.x;
            recognizerFrame.size.height += translation.y;

            recognizerFrame.origin.x += translation.x;
        }
        else if (recognizer.corner == BottomRightCorner)
        {
            recognizerFrame.size.width += translation.x;
            recognizerFrame.size.height += translation.y;
        }

        CGFloat minFrameSize = 40.0;
        CGFloat maxFrameWidth = self.cropView.frame.size.width;
        CGFloat maxFrameHeight = self.cropView.frame.size.height;

        if (recognizerFrame.size.width < minFrameSize)
        {
            recognizerFrame.size = CGSizeMake(minFrameSize, recognizerFrame.size.height);
        }

        if (recognizerFrame.size.height < minFrameSize)
        {
            recognizerFrame.size = CGSizeMake(recognizerFrame.size.width, minFrameSize);
        }

        if (recognizerFrame.size.width > maxFrameWidth)
        {
            recognizerFrame.size = CGSizeMake(maxFrameWidth, recognizerFrame.size.height);
        }

        if (recognizerFrame.size.height > maxFrameHeight)
        {
            recognizerFrame.size = CGSizeMake(recognizerFrame.size.width, maxFrameHeight);
        }

        [recognizer setTranslation:CGPointZero inView:self.cropView];
    }
}

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
    if (gestureRecognizer == self.cropBoxCornerPanRecognizer)
    {
        CropBoxCornerPanGestureRecognizer *recognizer = (CropBoxCornerPanGestureRecognizer *)gestureRecognizer;

        if (CGRectContainsPoint(self.cropView.cropBoxView.topLeftCorner.frame, [touch locationInView:self.cropView.cropBoxView]))
        {
            recognizer.corner = TopLeftCorner;

            return YES;
        }

        if (CGRectContainsPoint(self.cropView.cropBoxView.topRightCorner.frame, [touch locationInView:self.cropView.cropBoxView]))
        {
            recognizer.corner = TopRightCorner;

            return YES;
        }

        if (CGRectContainsPoint(self.cropView.cropBoxView.bottomLeftCorner.frame, [touch locationInView:self.cropView.cropBoxView]))
        {
            recognizer.corner = BottomLeftCorner;

            return YES;
        }

        if (CGRectContainsPoint(self.cropView.cropBoxView.bottomRightCorner.frame, [touch locationInView:self.cropView.cropBoxView]))
        {
            recognizer.corner = BottomRightCorner;

            return YES;
        }

        return NO;
    }

    return YES;
}