自動レイアウトを使用しているという質問から推測しています。自動レイアウトでは、先頭および/または上部の制約がある場合、 でスケーリングした後CGAffineTransformMakeScale
、先頭/上部の制約が再適用され、コントロールが移動します制約がまだ満たされていることを確認するため。
自動レイアウトをオフにする (これは簡単な答えです) か、次のことができます。
まで待ちviewDidAppear
ます (IB で定義された制約が適用され、コントロールが必要な場所に配置され、そのcenter
プロパティが信頼できるため);
問題のコントロールができたので、次のようにプロパティの値を使用してcenter
、先頭と上部の制約をNSLayoutAttributeCenterX
and制約に置き換えます。NSLayoutAttributeCenterY
center
constant
NSLayoutConstraint
したがって:
// don't try to do this in `viewDidLoad`; do it in `viewDidAppear`, where the constraints
// have already been set
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[self replaceLeadingAndTopWithCenterConstraints:self.imageView];
}
// Because our gesture recognizer scales the UIView, it's quite important to make
// sure that we don't have the customary top and leading constraints, but rather
// have constraints to the center of the view. Thus, this looks for leading constraint
// and if found, removes it, replacing it with a centerX constraint. Likewise if it
// finds a top constraint, it replaces it with a centerY constraint.
//
// Having done that, we can now do `CGAffineTransformMakeScale`, and it will keep the
// view centered when that happens, avoiding weird UX if we don't go through this
// process.
- (void)replaceLeadingAndTopWithCenterConstraints:(UIView *)subview
{
CGPoint center = subview.center;
NSLayoutConstraint *leadingConstraint = [self findConstraintOnItem:subview
attribute:NSLayoutAttributeLeading];
if (leadingConstraint)
{
NSLog(@"Found leading constraint");
[subview.superview removeConstraint:leadingConstraint];
[subview.superview addConstraint:[NSLayoutConstraint constraintWithItem:subview
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:subview.superview
attribute:NSLayoutAttributeTop
multiplier:1.0
constant:center.x]];
}
NSLayoutConstraint *topConstraint = [self findConstraintOnItem:subview
attribute:NSLayoutAttributeTop];
if (topConstraint)
{
NSLog(@"Found top constraint");
[subview.superview removeConstraint:topConstraint];
[subview.superview addConstraint:[NSLayoutConstraint constraintWithItem:subview
attribute:NSLayoutAttributeCenterY
relatedBy:NSLayoutRelationEqual
toItem:subview.superview
attribute:NSLayoutAttributeLeft
multiplier:1.0
constant:center.y]];
}
}
- (NSLayoutConstraint *)findConstraintOnItem:(UIView *)item attribute:(NSLayoutAttribute)attribute
{
// since we're looking for the item's constraints to the superview, let's
// iterate through the superview's constraints
for (NSLayoutConstraint *constraint in item.superview.constraints)
{
// I believe that the constraints to a superview generally have the
// `firstItem` equal to the subview, so we'll try that first.
if (constraint.firstItem == item && constraint.firstAttribute == attribute)
return constraint;
// While it always appears that the constraint to a superview uses the
// subview as the `firstItem`, theoretically it's possible that the two
// could be flipped around, so I'll check for that, too:
if (constraint.secondItem == item && constraint.secondAttribute == attribute)
return constraint;
}
return nil;
}
実装の詳細は、スケーリングするコントロールの制約をどのように定義したかによって異なる場合があります (私の場合、リーディングとトップはスーパービューに基づいていたため、簡単になりました)。これらの制約を削除し、中心に基づいて新しい制約を追加します。
上記のように、問題の制約を探して繰り返したくない場合は、IBOutlet
代わりにトップとリーディングの制約に対して を定義すると、プロセスが大幅に簡素化されます。このサンプル コードは、さまざまな理由で を参照に使用できなかったプロジェクトから取得したIBOutlet
ものNSLayoutConstraint
です。ただしIBOutlet
、制約の参照を使用する方が確実に簡単な方法です (自動レイアウトを使用する場合)。
たとえば、Interface Builder に移動すると、問題の制約を強調表示controlし、アシスタント エディターにドラッグして次のようにしますIBOutlet
。
これを行うと、すべての制約を繰り返すのではなく、次のように言うことができます。
if (self.imageViewVerticalConstraint)
{
[self.view removeConstraint:self.imageViewVerticalConstraint];
// create the new constraint here, like shown above
}
率直に言って、Interface Builder にこれらのような制約をすぐに定義できる機能があればいいのにと思います (つまり、「コントロールをスーパービューの左に導く」制約、「コントロールの中心をスーパービューの左に」という制約ではなく) が、私はIBでできるとは思わないので、プログラムで制約を変更しています。しかし、このプロセスを経ることで、コントロールをスケーリングできるようになり、制約のためにコントロールが動き回ることがなくなりました。
0x7fffffff が指摘したように、レイヤーに a を適用すると、制約が自動的に適用されないため、ビューCATransform3DMakeScale
に適用した場合のように移動することはありません。CGAffineTransformMakeScale
ただし、制約を再適用するために何かを行うと (setNeedsLayout
またはUIView
オブジェクトを変更すると、制約が再適用される可能性があります)、ビューが移動します。したがって、制約が再適用される前にレイヤーの変換を同一に戻すと、「こっそり侵入」できる可能性がありますが、自動レイアウトをオフにするか、制約を修正するのがおそらく最も安全です。