mam przypuszczenia od kwestii, że jesteś przy użyciu automatycznego układu: W układzie auto, jeśli masz wiodącą i/lub górne ograniczenie, po przeskalowaniu z CGAffineTransformMakeScale
, wiodący/top ograniczeniem będzie ponownie zastosowane, a twoja kontrola przeniesie się na ciebie, aby upewnić się, że ograniczenie jest nadal spełnione.
Można też wyłączyć układ automatycznego (która jest łatwa odpowiedź) lub można:
czekać aż viewDidAppear
(z powodu ograniczeń określonych w IB być zastosowane, a kontrola zostanie umieszczony tam, gdzie chcemy to i jego właściwość center
będzie wiarygodna);
teraz, że mamy center
z kontrolą, o którym mowa, należy wymienić wiodącą i górne ograniczenia z NSLayoutAttributeCenterX
i NSLayoutAttributeCenterY
ograniczeń, przy użyciu wartości dla center
własności ustawić constant
dla NSLayoutConstraint
jak w następujący sposób.
Zatem:
// 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;
}
Dane szczegółowe implementacji mogą się różnić w zależności od tego, jak zostały zdefiniowane ograniczenia dotyczące kontroli chcesz przeskalować (w moim przypadku, prowadząc i góry były oparte na superview, co ułatwiło), ale mam nadzieję, że ilustruje to rozwiązanie, aby usunąć te ograniczenia i dodać nowe oparte na centrum.
Możesz, jeśli nie chcesz iterować poprzez szukanie ograniczenia, o którym mowa, tak jak powyżej, zdefiniuj IBOutlet
dla górnych i wiodących wiązań, co znacznie upraszcza proces. Ten przykładowy kod został zaczerpnięty z projektu, w którym z różnych powodów nie mogłem użyć odniesień IBOutlet
. Ale użycie odniesień IBOutlet
dla wiązań jest zdecydowanie łatwiejszą metodą (jeśli pozostawisz układ automatyczny).
Na przykład, jeśli pójdziesz do konstruktora Interface, można wyróżnić ograniczenie w pytaniu i kontrola -Przeciągnij do asystent montażysty, aby Państwa IBOutlet
:
Jeśli to zrobisz, zamiast iteracja wszystkich ograniczeń, teraz można po prostu powiedzieć, na przykład:
if (self.imageViewVerticalConstraint)
{
[self.view removeConstraint:self.imageViewVerticalConstraint];
// create the new constraint here, like shown above
}
Szczerze życzę Interfejs Builder miał zdolność t o definiuj takie wiązania zaraz po wyjęciu z pudełka (tj. zamiast "ograniczenia kontroli na lewo od superwizji", ograniczenia "centrum kontroli na lewo od superwizji"), ale nie sądzę, że można to zrobić w IB, więc programowo modyfikuję swoje ograniczenia. Ale przechodząc przez ten proces, mogę teraz skalować kontrolę i nie pozwolić mi się poruszać ze względu na ograniczenia.
Jak 0x7fffffff zauważyć, jeśli stosuje się CATransform3DMakeScale
do warstwy, nie będzie automatycznie stosować ograniczeń, więc nie będzie widać jak się poruszać, jeśli stosuje CGAffineTransformMakeScale
do widoku. Ale jeśli zrobisz cokolwiek, aby ponownie zastosować ograniczenia (setNeedsLayout
lub jakiekolwiek zmiany w jakimkolwiek obiekcie UIView
mogą spowodować ponowne nałożenie ograniczeń), widok przeniesie się na ciebie. Więc możesz być w stanie "wkraść to", jeśli przywrócisz transformację warstwy do tożsamości zanim ograniczenia zostaną ponownie zastosowane, ale prawdopodobnie najbezpieczniej jest wyłączyć autolayout lub po prostu naprawić ograniczenia.
Czy jest jakiś powód, dla którego używasz animacji rdzenia do rotacji i grafiki rdzenia dla skali? Spróbowałbym również wykonać skalę na warstwie etykiety i sprawdzić, czy to pomaga. –
@ 0x7fffffff Właśnie przetestowałem: Jeśli zastosujesz "CATransform3DMakeScale" do warstwy, nie będzie ona automatycznie stosować ograniczeń, więc nie zobaczysz, jak się porusza, tak jakby zastosować 'CGAffineTransformMakeScale' do widoku. Ale jeśli zrobisz cokolwiek, aby ponownie zastosować ograniczenia ("setNeedsLayout" lub jakiekolwiek zmiany w jakichkolwiek obiektach "UIView" mogą spowodować ponowne nałożenie ograniczeń), widok przeniesie się na ciebie. Więc możesz być w stanie "wkraść to", jeśli przywrócisz transformację warstwy do tożsamości zanim ograniczenia zostaną ponownie zastosowane, ale prawdopodobnie najbezpieczniej jest wyłączyć autolayout lub po prostu naprawić ograniczenia. – Rob