使用UIPinchGestureRecognizer更改圆圈的大小

时间:2012-11-09 19:36:53

标签: objective-c ios cashapelayer uipinchgesturerecognizer

我在屏幕中央画了一个简单的圆圈:

int radius = 100;

- (void)addCircle {
    self.circle = [CAShapeLayer layer];
    self.circle.path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(0, 0, 2.0*radius, 2.0*radius)
                                             cornerRadius:radius].CGPath;
    self.circle.position = CGPointMake(CGRectGetMidX(self.view.frame)-radius,
                                  CGRectGetMidY(self.view.frame)-radius);

    self.circle.fillColor = [UIColor clearColor].CGColor;
    self.circle.strokeColor = [UIColor blackColor].CGColor;
    self.circle.lineWidth = 5;

    [self.view.layer addSublayer:self.circle];
}

使用捏合手势,我允许用户增加/减少形状的半径:

- (void)scale:(UIPinchGestureRecognizer *)gestureRecognizer {    
    if (gestureRecognizer.state != UIGestureRecognizerStateBegan) {
        if (gestureRecognizer.scale < lastScale) {
            --radius;
        }
        else if (gestureRecognizer.scale > lastScale) {
            ++radius;
        }
        // Center the shape in self.view
        self.circle.position = CGPointMake(CGRectGetMidX(self.view.frame)-radius, CGRectGetMidY(self.view.frame)-radius);

        self.circle.path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(0, 0, 2.0*radius, 2.0*radius) cornerRadius:radius].CGPath;
    }

    lastScale = gestureRecognizer.scale;
}

然而,圆圈不会停留在死亡中心。相反,它会在中间反弹,直到手势结束才会稳定下来。

有谁知道为什么会这样,如果有,我怎么能阻止它?

1 个答案:

答案 0 :(得分:2)

您的代码中存在一些问题。作为@tc。说,你没有设置形状图层的框架(或边界)。默认图层大小为CGSizeZero,这就是为什么每次更改半径时都必须将图层的位置偏移半径。

此外,形状图层的positionpath属性是可动画的。因此,默认情况下,当您更改它们时,Core Animation会将它们设置为新值。路径动画会导致您的不良行为。

此外,您应该根据self.view.bounds设置图层的位置或框架,而不是self.view.frame,因为图层的位置/框架是self.view的坐标系,而不是坐标系self.view.superview。如果self.view是顶级视图并且您支持界面自动旋转,则无关紧要。

我建议您修改一下如何实现这一点。将radius设为CGFloat属性,并设置属性更新图层的boundspath

@interface ViewController ()

@property (nonatomic, strong) CAShapeLayer *circle;
@property (nonatomic) CGFloat radius;

@end

@implementation ViewController

- (void)setRadius:(CGFloat)radius {
    _radius = radius;
    self.circle.bounds = CGRectMake(0, 0, 2 * radius, 2 * radius);
    self.circle.path = [UIBezierPath bezierPathWithOvalInRect:self.circle.bounds].CGPath;
}

如果你真的想强制半径为整数,我建议内部跟踪它作为一个浮点数,因为如果它是一个浮点数,用户交互会更顺畅。在为边界和路径创建CGRect之前,只需在临时变量中对其进行舍入:

    CGFloat intRadius = roundf(radius);
    self.circle.bounds = CGRectMake(0, 0, 2 * intRadius, 2 * intRadius);
    self.circle.path = [UIBezierPath bezierPathWithOvalInRect:self.circle.bounds].CGPath;

addCircle中,只需设置radius属性,让该setter负责设置图层的边界和路径。还要推迟设置图层的位置,直到系统的布局阶段。这样,在界面旋转后,您将再次将圆圈重新定位在中心。

- (void)viewDidLoad {
    [super viewDidLoad];
    [self addCircle];
}

- (void)addCircle {
    self.circle = [CAShapeLayer layer];
    self.circle.fillColor = nil;
    self.circle.strokeColor = [UIColor blackColor].CGColor;
    self.circle.lineWidth = 5;
    self.radius = 100;
    [self.view.layer addSublayer:self.circle];
    [self.view setNeedsLayout];
}

- (void)viewDidLayoutSubviews {
    [super viewDidLayoutSubviews];
    self.circle.position = CGPointMake(CGRectGetMidX(self.view.bounds), CGRectGetMidY(self.view.bounds));
}

最后,要处理捏合手势,只需将新半径设置为旧半径乘以手势的比例。 radius setter将负责更新图层的路径和边界。然后将手势的比例重置为1.这比跟踪手势的先前比例更简单。另外,使用CATransaction禁用path属性的动画。

- (IBAction)pinchGestureWasRecognized:(UIPinchGestureRecognizer *)recognizer {
    [CATransaction begin]; {
        [CATransaction setDisableActions:YES];
        self.radius *= recognizer.scale;
        recognizer.scale = 1;
    } [CATransaction commit];
}