3

重複の可能性:
位置を変更せずに CGPath を回転する

さまざまなコードを数時間検索してテストしましたが、これを機能させることができません。

ビューに追加される CAShapeLayer に任意の UIBezierPath をランダムな場所に追加しています。デバイスの回転を処理できるように、パスを回転させる必要があります。パスの代わりにレイヤーを回転できます。結果を回転させるだけです。

スケーリングと変換によってベジエ パスの変換を処理するメソッドは既にあります。それはうまく機能しますが、今度は左または右に 90 度回転する必要があります。

これを行う方法に関する推奨事項はありますか?

基本コード:

UIBezierPath *path = <create arbitrary path>
CAShapeLayer *layer = [CAShapeLayer layer];
[self addPathToLayer:layer
             fromPath:path];

// I could get the center of the box but where is the box center for the view it is in?
// CGRect box = CGPathGetPathBoundingBox(path.CGPath);

// layer.anchorPoint = ? How to find the center of the box for the anchor point?

// Rotating here appears to rotate around 0,0 of the view
layer.transform = CATransform3DMakeRotation(DegreesToRadians(-90), 0.0, 0.0, 1.0);

次の投稿が表示されます: BezierPath Rotation in a UIView

そのまま回転させてから、パスを元の位置に戻すことができると思います。翻訳値がどうなるかを把握する必要があります。

また、回転しようとした後に表示されるのは、画像が画面外のどこかに移動していることです。動きを確認するために 25 度回転させてみましたが、ビューの原点 0,0 を中心に回転するので、90 度回転すると画像が画面からはみ出してしまいます。デバイスを回転させずにこれらのテストを実行しています-回転がどのように機能するかを確認するためだけです。

更新 #1 - 2012 年 12 月 4 日: いくつかの奇妙な理由で、位置を経験的に見つけた値に設定すると、回転後のベジエ パスが正しい位置に移動します。

layer.position = CGPointMake(280, 60);

この値は、アプリの開始/停止と調整からの推測です。回転時に位置を調整する必要がある理由がわかりません。アンカーポイントはレイヤーの中心にある必要があります。ただし、パスが設定されていても CAShapeLayer のフレームと位置の両方がすべてゼロであり、パスがビュー内の正しい位置にあることもわかりました。280, 60 の位置は、+90 の回転が行われたときにパスのバウンディング ボックスの中心になる位置にパスをシフトします。回転値を変更する場合は、位置を調整する必要があります。この手動調整を行う必要はありません。

最後の手段は、何らかの形でベジエ パスを画像に変換してから追加することだと思います。レイヤーのコンテンツを画像に設定してから回転すると、位置調整を必要とせずに中心点を中心に回転することがわかりました。パスの設定ではそうではありません。

UPDATE #2 12/4/2012 - フレームを設定しようとしましたが、いじって次のように中央に配置しました。

CGRect box = CGPathGetPathBoundingBox(path.CGPath);
CGRect rect = CGRectMake(0, 0, box.origin.x + (3.5 * box.size.width), box.origin.y + (3.5 * box.size.height));
layer.frame = rect;
layer.transform = CATransform3DMakeRotation(DegreesToRadians(90), 0.0, 0.0, 1.0);

なぜ 3.5 倍するのですか? 私は見当もつかない。ボックスのサイズの約 3.5 倍のボックスの原点を追加すると、回転した CAShapeLayer パスが本来あるべき場所に移動することがわかりました。

これを行うためのより良い方法があるはずです。フレームサイズは回転角度に依存しないため、これは以前の投稿よりも優れたソリューションです。フレームを設定している値に設定する必要がある理由がわかりません。私はそれが CGRectMake(0, 0, box.origin.x + (box.size.width / 2), box.origin.y + (box.size.height / 2)); であるべきだと思った

ただし、画像を左にシフトしすぎます。

私が見つけたもう1つの手がかりは、[self view].frame(iPhoneの画面である親ビュー全体のフレーム)のフレームを設定してから回転させると、回転ポイントが画面の中心になり、パス/画像はこの中心点の周りを周回します。これが、ボックスの中心を周回するように、フレームをパスの中心に移動しようとした理由です。

UPDATE #3 12/4/2012 - レイヤーを画像としてレンダリングしようとしました。ただし、レイヤーのパスを設定するだけでは、レイヤーが空であるため、レイヤー内の「画像」にはなりません。

    CGRect box = CGPathGetPathBoundingBox(path.CGPath);
    layer.frame = box;

    UIImage *image = [ImageHelper imageFromLayer:layer]; // ImageHelper library I created  

    CAShapeLayer *newLayer = [CAShapeLayer layer];
    newLayer.frame = CGRectMake(box.origin.x, box.origin.y, image.size.width, image.size.height);
    newLayer.contents = (id) image.CGImage;

パス セットを使用してレイヤーを回転させることは、単にベジエ パス自体を回転させることと変わらないようです。ベジエ パスの回転に戻り、位置要素などをいじることができるかどうかを確認します。これには解決策が必要です。

目標: 最初に作成されたビュー内の中心点を中心に UIBezierPath を回転させます。

UPDATE #4 12/4/2012 - UIBezierPath を以前の中心位置に配置するために、翻訳に必要な値を測定する一連のテストを実行しました。

CGAffineTransform rotate = CGAffineTransformMakeRotation(DegreesToRadians(-15));
[path applyTransform:rotate];

// CGAffineTransform translate = CGAffineTransformMakeTranslation(-110, 70); // -45
CGAffineTransform translate = CGAffineTransformMakeTranslation(-52, -58); // -15
[path applyTransform:translate];

ただし、x/y 平行移動の比率は対応していないため、角度に基づいて必要な平行移動を推定することはできません。「CGAffineTransformMakeRotation」は、回転を行うために配置された任意のアンカーを使用しているように見えますが、現時点ではおそらく (viewWidth / 2, 0) のように見えます。私はこれを必要以上に難しくしています。中心点が維持されるように単純な回転を行うのに欠けているものがあります。パスを左または右に 90 度「スピン」するだけです。

UPDATE #5 12/4/2012 - 追加のテストを実行した後、UIBezierPath を回転させるためのアンカー ポイントが、すべてのポイントが描画された起点であることがわかりました。この場合、原点は 0,0 であり、すべての点はその点を基準としています。したがって、回転が適用されると、回転は原点の周りで発生し、パスが -90 度で右上に、90 度で左上にシフトする理由です。回転のアンカー ポイントを中心に設定する必要があるので、元の原点ではなく、中心を中心に「回転」します。この 1 つの問題に 12 時間を費やしました。

4

1 に答える 1

5

いくつかの詳細な分析と紙の上の境界ボックスのグラフ化の後、0,0 の原点が正しいという私の主張を見つけました。

この問題の解決策は、バウンディング ボックスの中心を原点にして、パス (基になるマトリックス) を原点に移動し、回転させてから、パスを元の位置に戻すことです。

UIBezierPath を 90 度回転させる方法は次のとおりです。

CGAffineTransform translate = CGAffineTransformMakeTranslation(-1 * (box.origin.x + (box.size.width / 2)), -1 * (box.origin.y + (box.size.height / 2)));
[path applyTransform:translate];

CGAffineTransform rotate = CGAffineTransformMakeRotation(DegreesToRadians(90));
[path applyTransform:rotate];

translate = CGAffineTransformMakeTranslation((box.origin.x + (box.size.width / 2)), (box.origin.y + (box.size.height / 2)));
[path applyTransform:translate];

-90 度差し込むと反対方向に回転します。

この式は、デバイスを縦向きから横向き、またはその逆に回転するときに使用できます。

これが理想的な解決策だとはまだ思いませんが、結果は今のところ必要なものです。

誰かがこれに対するより良い解決策を持っている場合は、投稿してください。

2012 年 12 月 7 日更新 - 私が考える最良の解決策が見つかりました。ベジェ パスで回転、移動、およびスケーリング メソッドを使用する代わりに、ポイントの配列を CGPoint オブジェクトとして抽出し、ビューのサイズと方向に基づいて、必要に応じてそれらをスケーリング/移動します。次に、新しいベジエ パスを作成し、レイヤーをこのパスに設定します。

その結果、完璧なスケーリング、移動、回転が実現します。

于 2012-12-05T03:41:03.563 に答える