[編集:警告:次のディスカッション全体がiOS 8によって時代遅れになるか、少なくとも大幅に緩和される可能性があります。これにより、ビューの変換が適用されたときにレイアウトをトリガーするミスを犯すことがなくなります。]
自動レイアウトとビュー変換
Autolayoutはビュー変換ではまったくうまく機能しません。私が識別できる限り、その理由は、変換(デフォルトの恒等変換以外)を持つビューのフレームをいじる必要がないためです-しかし、それはまさにautolayoutが行うことです。autolayoutが機能する方法はlayoutSubviews
、ランタイムですべての制約をダッシュし、それに応じてすべてのビューのフレームを設定することです。
つまり、制約は魔法ではありません。彼らはただのやることリストです。layoutSubviews
やることリストが行われる場所です。そして、それはフレームを設定することによってそれを行います。
これをバグとみなすのは仕方ない。この変換をビューに適用すると、次のようになります。
v.transform = CGAffineTransformMakeScale(0.5,0.5);
以前と同じ場所に、半分のサイズでビューの中心が表示されることを期待しています。しかし、その制約にもよりますが、私にはまったく見えないかもしれません。
[実際、2つ目の驚きがあります。ビューに変換を適用すると、レイアウトがすぐにトリガーされます。これは別のバグのようです。または、おそらくそれが最初のバグの中心です。私が期待するのは、レイアウト時間までフレームアニメーションを回避できるのと同じように、デバイスが回転するなど、少なくともレイアウト時間まで変換を回避できることです。しかし、実際には、レイアウト時間は即時であり、これは間違っているようです。]
解決策1:制約なし
現在の解決策の1つは、ビューに影響を与えるすべての制約を削除するために、半永久的な変換をビューに適用する(そして、一時的に何らかの方法で振るだけではない)場合です。残念ながら、これは通常、自動レイアウトがまだ行われているため、ビューが画面から消えてしまい、ビューを配置する場所を指示する制約がなくなりました。したがって、制約を削除することに加えて、ビューtranslatesAutoresizingMaskIntoConstraints
をYES に設定します。ビューは古い方法で機能し、実質的に自動レイアウトの影響を受けません。(それはさ、明らかに、自動レイアウトの影響を受けますが、暗黙の自動サイズ変更マスクの制約は、その行動は、それが自動レイアウトの前にしただけのようにさせます。)
解決策2:適切な制約のみを使用する
それが少し抜本的であると思われる場合、別の解決策は、目的の変換で正しく機能するように制約を設定することです。たとえば、ビューのサイズが内部の固定幅と高さだけで決まり、純粋に中心で配置されている場合、スケール変換は期待どおりに機能します。このコードでは、サブビュー(otherView
)の既存の制約を削除し、それらを4つの制約に置き換えて、幅と高さを固定し、純粋に中心で固定しています。その後、私のスケール変換が機能します:
NSMutableArray* cons = [NSMutableArray array];
for (NSLayoutConstraint* con in self.view.constraints)
if (con.firstItem == self.otherView || con.secondItem == self.otherView)
[cons addObject:con];
[self.view removeConstraints:cons];
[self.otherView removeConstraints:self.otherView.constraints];
[self.view addConstraint:
[NSLayoutConstraint constraintWithItem:self.otherView attribute:NSLayoutAttributeCenterX relatedBy:0 toItem:self.view attribute:NSLayoutAttributeLeft multiplier:1 constant:self.otherView.center.x]];
[self.view addConstraint:
[NSLayoutConstraint constraintWithItem:self.otherView attribute:NSLayoutAttributeCenterY relatedBy:0 toItem:self.view attribute:NSLayoutAttributeTop multiplier:1 constant:self.otherView.center.y]];
[self.otherView addConstraint:
[NSLayoutConstraint constraintWithItem:self.otherView attribute:NSLayoutAttributeWidth relatedBy:0 toItem:nil attribute:0 multiplier:1 constant:self.otherView.bounds.size.width]];
[self.otherView addConstraint:
[NSLayoutConstraint constraintWithItem:self.otherView attribute:NSLayoutAttributeHeight relatedBy:0 toItem:nil attribute:0 multiplier:1 constant:self.otherView.bounds.size.height]];
要するに、ビューのフレームに影響を与える制約がない場合、自動レイアウトはビューのフレームに影響を与えません。これは、変換が関係しているときに必要なことです。
解決策3:サブビューを使用する
上記の両方のソリューションの問題は、ビューを配置するための制約の利点を失うことです。これを解決するソリューションがあります。ホストとしてのみ機能するジョブの非表示ビューから開始し、制約を使用して配置します。その中に、実際のビューをサブビューとして配置します。制約を使用して、サブビューをホストビュー内に配置しますが、これらの制約は、変換を適用したときに反発しない制約に制限します。
これがイラストです:
白いビューはホストビューです。あなたはそれが透明で、それゆえ目に見えないふりをすることになっています。赤いビューは、そのビューの中心をホストビューの中心に固定することによって配置されたサブビューです。これで、赤いビューをその中心の周りで問題なくスケールおよび回転できます。実際、図はそうしたことを示しています。
self.otherView.transform = CGAffineTransformScale(self.otherView.transform, 0.5, 0.5);
self.otherView.transform = CGAffineTransformRotate(self.otherView.transform, M_PI/8.0);
その間、ホストビューの制約により、デバイスを回転させてもホストビューは正しい場所に保持されます。
解決策4:代わりにレイヤー変換を使用する
ビュー変換の代わりに、レイアウトをトリガーせず、制約との即時の競合を引き起こさないレイヤー変換を使用します。
たとえば、次の単純な「ドキドキ」ビューのアニメーションは、自動レイアウトではうまく機能しない可能性があります。
[UIView animateWithDuration:0.3 delay:0
options:UIViewAnimationOptionAutoreverse
animations:^{
v.transform = CGAffineTransformMakeScale(1.1, 1.1);
} completion:^(BOOL finished) {
v.transform = CGAffineTransformIdentity;
}];
最終的にはビューのサイズに変更はありませんでしたが、設定するだけでtransform
レイアウトが発生し、制約によってビューがジャンプすることがあります。(これはバグのように感じますか?)しかし、コアアニメーションで同じことを(CABasicAnimationを使用し、アニメーションをビューのレイヤーに適用して)行うと、レイアウトは行われず、正常に機能します。
CABasicAnimation* ba = [CABasicAnimation animationWithKeyPath:@"transform"];
ba.autoreverses = YES;
ba.duration = 0.3;
ba.toValue = [NSValue valueWithCATransform3D:CATransform3DMakeScale(1.1, 1.1, 1)];
[v.layer addAnimation:ba forKey:nil];
layerView
てその幅を知るのですか?それは何か他のものにその右側を突き刺していますか?