0

画面の中央に単純な円を描いています。

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

ただし、円は真ん中にとどまりません。代わりに、それは真ん中で跳ね返り、ジェスチャが終了するまで落ち着きません。

なぜこれが起こっているのか、もしそうなら、どうすればそれを防ぐことができるのか知っていますか?

4

1 に答える 1

2

コードにいくつかの問題があります。@tc として。シェイプレイヤーのフレーム(または境界)を設定していません。デフォルトのレイヤー サイズは ですCGSizeZero。これが、半径を変更するたびにレイヤーの位置を半径だけオフセットする必要がある理由です。

また、シェイプ レイヤーのpositionおよびpathプロパティはアニメート可能です。したがって、デフォルトでは、それらを変更すると、Core Animation はそれらを新しい値にアニメーション化します。パス アニメーションが望ましくない動作の原因になっています。

self.view.boundsまた、レイヤの位置またはフレームはではなくに基づいて設定する必要がありますself.view.frame。これは、レイヤの位置/フレームが の座標系でself.viewはなく の座標系であるためself.view.superviewです。self.viewが最上位ビューであり、インターフェースの自動回転をサポートしている場合、これは問題になります。

これを実装する方法を修正することをお勧めします。プロパティを作成しradiusCGFloatプロパティを設定してレイヤーのboundsと を更新しpathます。

@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で、そのセッターにレイヤーの境界とパスの設定を任せます。また、システムのレイアウト フェーズまでレイヤーの位置の設定を延期します。そうすれば、インターフェイスを回転させた後、円を再び中心に再配置できます。

- (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パスと境界を更新します。次に、ジェスチャのスケールを 1 にリセットします。これは、ジェスチャの以前のスケールを追跡するよりも簡単です。また、プロパティCATransactionのアニメーションを無効にするために使用しpathます。

- (IBAction)pinchGestureWasRecognized:(UIPinchGestureRecognizer *)recognizer {
    [CATransaction begin]; {
        [CATransaction setDisableActions:YES];
        self.radius *= recognizer.scale;
        recognizer.scale = 1;
    } [CATransaction commit];
}
于 2012-11-10T02:37:21.443 に答える