5

パン、ピンチ、回転 UIGestureRecognizers を使用して、ユーザーが特定の UI 要素を必要な場所に正確に配置できるようにしています。ここからのコードを使用http://www.raywenderlich.com/6567/uigesturerecognizer-tutorial-in-ios-5-pinches-pans-and-more (またはここからの同様のコードhttp://mobile.tutsplus.com/ tutorials/iphone/uigesturerecognizer/ ) 両方とも、ユーザーがこれらの UI 要素を必要に応じて配置するために必要なものを提供してくれます。

ユーザーが「UI レイアウト」モードを終了すると、UIView の変換と中央を次のように保存します。

NSString *transformString = NSStringFromCGAffineTransform(self.transform);
[[NSUserDefaults standardUserDefaults] setObject:transformString forKey:@"UItransform", suffix]];

NSString *centerString = NSStringFromCGPoint(self.center);
[[NSUserDefaults standardUserDefaults] setObject:centerString forKey:@"UIcenter"];

アプリをリロードすると、UIView の変換と中央が次のように読み取られます。

NSString *centerString = [[NSUserDefaults standardUserDefaults] objectForKey:@"UIcenter"];
if( centerString != nil )
    self.center = CGPointFromString(centerString);

NSString *transformString = [[NSUserDefaults standardUserDefaults] objectForKey:@"UItransform"];

if( transformString != nil )
    self.transform = CGAffineTransformFromString(transformString);

そして、UIView は正しく回転およびスケーリングされますが、間違った場所に配置されます。さらに、「UI レイアウト」モードに再び入ると、さまざまなジェスチャで常にビューを取得できるとは限りません (表示されるビューがジェスチャ認識エンジンによって理解されるビューではないかのように)。

また、UIViewtransformを ID に設定するリセット ボタンもありcenter、NIB から読み込まれたときの状態に設定されます。しかし、変更された UIView センターと変換をロードした後、リセットしても機能しません。UIView の位置が間違っています。

私が最初に考えたのは、これらのジェスチャ コードの例が変更されているためcenter、回転は異なる中心の周りで発生しているに違いないということでした (移動、回転、スケールの予測不可能なシーケンスを想定しています)。編集のシーケンス全体を保存したくないので (レイアウト モードで元に戻す機能が必要な場合は便利かもしれませんが)、変換を使用して移動するように UIPanGestureRecognizer ハンドラーを変更しました。それが機能するようになったら、変換を保存するだけで、物事がどのような順序で起こったかに関係なく、現在の位置と方向が得られると考えました。しかし、そのような幸運はありません。私はまだこの方法で奇抜な位置を取得します。

だから私は途方に暮れています。UIView が移動され、新しい位置に回転された場合、後でロードして UIView をあるべき場所に戻すことができるように、その場所と向きを保存するにはどうすればよいですか?

この権利をタグ付けしなかった場合、正しくレイアウトしなかった場合、または他のスタックオーバーフローの罪を犯した場合は、事前に謝罪してください。ここに投稿するのは初めてです。

編集

これまでのところ、2つの提案を試しています。それらは事実上同じものだと思います(1つはフレームを保存することを提案し、もう1つは原点を保存することを提案します。これはframe.originだと思います)。

したがって、設定コードからの保存/読み込みコードには次のものが含まれます。

保存:

NSString *originString = NSStringFromCGPoint(self.frame.origin);
[[NSUserDefaults standardUserDefaults] setObject:originString forKey:@"UIorigin"];

読み込み (変換を読み込む前):

NSString *originString = [[NSUserDefaults standardUserDefaults] objectForKey:@"UIorigin"];
if( originString ) {
    CGPoint origin = CGPointFromString(originString);
    self.frame = CGRectMake(origin.x, origin.y, self.frame.size.width, self.frame.size.height);
}

私は同じ(または似たような - 分かりにくい)結果を得ます。実際、設定をリロードするだけのボタンを追加しました。ビューが回転すると、その「リロード」ボタンはUIViewをオフセットで繰り返し移動します(フレームまたは変換がそれ自体に関連しているかのように-私は確信しています)は手がかりですが、それが何を指しているのかわかりません)。

編集#2

これは、ビューのframe. Apple http://developer.apple.com/library/ios/#documentation/WindowsViews/Conceptual/ViewPG_iPhoneOS/WindowsandViews/WindowsandViews.html#//apple_ref/doc/uid/TP40009503-CH2-SW6 (強調鉱山) から:

ビューの変換にスケーリングまたは回転係数が追加されている場合でも、center プロパティの値は常に有効です。ビューの変換が恒等変換と等しくない場合、フレーム プロパティの値は無効と見なされます。

編集#3

さて、設定を読み込んでいるときは、すべて問題ないようです。UI パネルのboundsrect は {{0, 0}, {506, 254}} です。私の VC の viewDidLoad メソッドの最後では、すべて問題ないようです。しかし、実際に物が表示されるまでにboundsは、別のものがあります。例: {{0, 0}, {488.321, 435.981}} (これは、回転およびスケーリングされた後のスーパービュー内の大きさのように見えます)。境界を本来あるべき状態にリセットすると、元の位置に戻ります。

プログラムで境界を本来あるべき状態にリセットするのは簡単ですが、実際にはいつそれを行うべきかわかりません! 私はviewDidLoadの最後にそれをやろうと思っていたでしょうが、boundsその時点ではまだ正しいです。

編集#4

私は(NIBから来ているように)initWithCoderでself.boundsをキャプチャしてから、layoutSubviewsでself.boundsをそのキャプチャされたCGRectにリセットしようとしました。そして、それは機能します。

しかし、それは恐ろしくハックで、危険に満ちているようです。これは本当に正しい方法ではありません。(できますか?) 以下の skram の回答は非常に単純に見えますが、アプリをリロードすると機能しません。

4

5 に答える 5

2

プロパティも保存しframeます。NSStringFromCGRect()とを使用できますCGRectFromString()

読み込み時に、フレームを設定してから変換を適用します。これは、私のアプリの1つで行う方法です。

お役に立てれば。

更新: 私の場合、UIViews回転とサイズ変更を適用できる Draggable があります。NSCoding以下の例のように、オブジェクトを保存してロードするために使用します。

//encoding
....
[coder encodeCGRect:self.frame forKey:@"rect"];
// you can save with NSStringFromCGRect(self.frame);
[coder encodeObject:NSStringFromCGAffineTransform(self.transform) forKey:@"savedTransform"];

//init-coder
CGRect frame = [coder decodeCGRectForKey:@"rect"];
// you can use frame = CGRectFromString(/*load string*/);
[self setFrame:frame];
self.transform = CGAffineTransformFromString([coder decodeObjectForKey:@"savedTransform"]);

これが行うことは、フレームと変換を保存し、必要に応じてロードすることです。NSStringFromCGRect()とで同じ方法を適用できますCGRectFromString()

更新 2 : あなたの場合。あなたはこのようなことをするでしょう..

[self setFrame:CGRectFromString([[NSUserDefaults standardUserDefaults] valueForKey:@"UIFrame"])];
self.transform = CGAffineTransformFromString([[NSUserDefaults standardUserDefaults] valueForKey:@"transform"]);

、およびキーで保存しNSUserDefaultsていると仮定します。UIFrametransform

于 2012-06-13T05:05:09.233 に答える
2

問題を再現できません。次のコードを使用しました。これは次のことを行います。

  • ビューを追加します
  • 中心をずらして移動
  • 変換でスケーリングします
  • 最初に連結された別の変換でそれを回転させます
  • 変換と中心を文字列に保存します
  • 別のビューを追加し、文字列から中心と変換を適用します

これにより、まったく同じ場所と位置に 2 つのビューが作成されます。

- (void)viewDidLoad
{
    [super viewDidLoad];

    UIView *view1 = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
    view1.layer.borderWidth = 5.0;
    view1.layer.borderColor = [UIColor blackColor].CGColor;
    [self.view addSubview:view1];
    view1.center = CGPointMake(150,150);
    view1.transform = CGAffineTransformMakeScale(1.3, 1.3);
    view1.transform = CGAffineTransformRotate(view1.transform, 0.5);

    NSString *savedCentre = NSStringFromCGPoint(view1.center);
    NSString *savedTransform = NSStringFromCGAffineTransform(view1.transform);

    UIView *view2 = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
    view2.layer.borderWidth = 2.0;
    view2.layer.borderColor = [UIColor greenColor].CGColor;
    [self.view addSubview:view2];
    view2.center = CGPointFromString(savedCentre);
    view2.transform = CGAffineTransformFromString(savedTransform);

}

与える:

ここに画像の説明を入力

これは、すべての変換が中心点の周りで発生するため、影響を受けないという点で、ドキュメントから期待されることと結びついています。アイテムを以前の状態に復元できないと想像できる唯一の方法は、スーパービューが独自の変換または別のフレーム、またはまったく別のビューで何らかの形で異なっていた場合です。しかし、私はあなたの質問からそれを伝えることはできません.

要約すると、質問の元のコードは機能しているはずなので、何か他のことが起こっています! うまくいけば、この回答が絞り込むのに役立ちます。

于 2012-06-13T18:33:54.623 に答える
1

UIViewの場所も保存する必要があります。

CGPoint position = CGPointMake(self.view.origin.x, self.view.origin.y)

NSString _position = NSStringFromCGPoint(position);

// Do the saving
于 2012-06-13T05:11:39.753 に答える
0

回答を提供してくれたすべての人に感謝します!それらすべての合計は私を次のように導きました:

問題はbounds、起動時に設定から変換をロードした後にCGRectがリセットされていたが、リアルタイムで変更しながら設定を更新したときではなかったようです。

2つの解決策があると思います。layoutSubviews1つは、からではなく、からプリファレンスを最初にロードすることviewDidLoadです。と呼ばれたbounds後は何も起こらないようです。layoutSubviews

ただし、私のアプリの他の理由により、ViewControllerのから設定をロードする方が便利viewDidLoadです。したがって、私が使用しているソリューションは次のとおりです。

// UserTransformableView.h
@interface UserTransformableView : UIView {
    CGRect defaultBounds;
}

// UserTransformableView.m
- (id)initWithCoder:(NSCoder *)aDecoder {
    self = [super initWithCoder:aDecoder];
    if( self ) {
        defaultBounds = self.bounds;
    }
    return self;
}

- (void)layoutSubviews {
    self.bounds = defaultBounds;
}
于 2012-06-21T21:43:44.887 に答える
0

何が起こっているのかはわかりませんが、役立つアイデアがいくつかあります。

1- スクラムの解決策はもっともらしく思えboundsますが、保存したいのは ではなく、frameです。(回転がない場合、中心と境界がフレームを定義することに注意してください。したがって、2つを設定することはフレームを設定することと同じです。)から、リンクしたIOSのビュープログラミングガイド:

重要 ビューの変換プロパティが恒等変換でない場合、そのビューのフレーム プロパティの値は未定義であり、無視する必要があります。ビューに変換を適用する場合、ビューの境界と中心のプロパティを使用して、ビューのサイズと位置を取得する必要があります。サブビューのフレーム四角形は、ビューの境界に対して相対的であるため、引き続き有効です。

2- 別のアイデア。アプリをリロードすると、次のことを試すことができます。

  • まず、ビューの変換を恒等変換に設定します。
  • 次に、ビューの境界と中心を保存された値に設定します。
  • 最後に、ビューの変換を保存済みの変換に設定します。

アプリを再起動する場所によっては、一部の古いジオメトリで再起動する場合があります。これで何かが変わるとは思いませんが、試すのは簡単です。
更新: いくつかのテストの後、これは何の効果もないように見えます。変換を変更しても、境界や中心は変更されないようです (ただし、フレームは変更されます)。

bounds3- 最後に、ピンチ ジェスチャ レコグナイザをではなく で動作するように書き直すことで、問題を回避できますtransform。(繰り返しますが、以前の回転では無効になる可能性があるため、boundsではなくを使用してください。) このように、変換は回転にのみ使用されます。これは、再描画せずに他の方法で行うことはできないと思います。 frameframe

同じガイドから、Apple の推奨事項は次のとおりです。

アニメーションを実装する場合は、通常、ビューの変換プロパティを変更します。たとえば、このプロパティを使用して、ビューの中心点を中心に回転するアニメーションを作成できます。スーパービューの座標空間内でビューの位置やサイズを変更するなど、ビューに永続的な変更を加えるためにこのプロパティを使用しないでください。そのタイプの変更については、代わりにビューのフレーム四角形を変更する必要があります。

于 2012-06-13T10:06:36.973 に答える