2013-04-29 10 views
13

W oparciu o pytanie miałem earlier.iOS: CGAffineTransformScale przenosi mój obiekt

Prosty przycisk próbujący przekształcić etykietę. Chcę, żeby zmniejszyło się o 0,5, co działa, ale z jakiegoś powodu również przesuwa obiekt, tak jak to robi. Etykieta przeskakuje w lewo i następnie przekształca.

- (IBAction)btnTest:(id)sender 
{ 

    [UIView animateWithDuration:1 delay:0 options:UIViewAnimationOptionCurveEaseInOut animations:^{ 
     lblTest.transform = CGAffineTransformScale(lblTest.transform, 0.5f,0.5f); 
    }completion:^(BOOL finished) { 
     if(finished){ 
      NSLog(@"DONE"); 
     } 
    }]; 
} 
+1

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. –

+0

@ 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

Odpowiedz

18

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:

make constraint 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.

+0

Bardzo wyczerpująca odpowiedź! Dzięki! Zajmie mi trochę czasu, aby zrozumieć całość, ale dziękuję za poświęcenie czasu na odpowiedź! – JoshDG

+0

Bardzo hepfull odpowiedź !!! Uratowałeś mój dzień! – Amnysia

+0

Właśnie zmieniłem górne i wiodące ograniczenia na wiązania środkowe w scenorysie, ale podobny problem nadal się pojawia, oczekuję, że zamiast przeskakiwać w lewo, widok nie jest po prostu trzymany z prawej strony superview (gdzie powinien być), porusza się równomiernie do swojej pozycji, ale podczas animacji zawsze znajduje się w niewłaściwej pozycji =/ –

Powiązane problemy