2013-04-29 4 views
13

Aufbauend auf einer Frage hatte ich earlier.iOS: CGAffineTransformScale bewegt mein Objekt

Einfache Schaltfläche versucht, ein Etikett zu transformieren. Ich möchte, dass es um 0,5 schrumpft, was funktioniert, aber aus irgendeinem Grund bewegt es auch das Objekt so, wie es es tut. Das Label springt nach oben und nach links, dann transformiert es sich.

- (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

Gibt es einen Grund, warum Sie Core-Animation für die Rotation und Core-Grafiken für die Skala verwenden? Ich würde versuchen, die Skala auch auf der Ebene des Labels durchzuführen und zu sehen, ob das hilft. –

+0

@ 0x7fffffff Ich habe es gerade getestet: Wenn Sie eine 'CATransform3DMakeScale' auf die Ebene anwenden, werden die Abhängigkeiten nicht automatisch angewendet, so dass Sie nicht sehen, dass sie sich bewegt, wenn Sie 'CGAffineTransformMakeScale' auf die Ansicht anwenden. Wenn Sie jedoch etwas tun, um Constraints erneut anzuwenden ('setNeedsLayout' oder irgendwelche Änderungen an irgendwelchen' UIView'-Objekten können dazu führen, dass die Constraints erneut angewendet werden), wird die Ansicht auf Ihnen weiterlaufen. Sie können sich also möglicherweise "einschmuggeln", wenn Sie die Transformation des Layers wieder in die Identität zurückversetzen, bevor die Constraints erneut angewendet werden. Am sichersten ist es jedoch, das automatische Layout auszuschalten oder die Einschränkungen zu beheben. – Rob

Antwort

18

Ich bin von der Frage der Annahme, dass Sie Auto-Layout verwenden: Im Auto-Layout, wenn Sie eine führende und/oder Top-Einschränkung haben, nachdem Sie mit CGAffineTransformMakeScale, die führenden/top Einschränkung skaliert werden erneut angewendet und Ihre Kontrolle wird auf Sie übertragen, um sicherzustellen, dass die Einschränkung noch erfüllt ist.

können Sie entweder die automatische Layout ausschalten (das ist die einfache Antwort ist) oder Sie können:

  • warten, bis viewDidAppear (weil in IB definiert Einschränkungen angewendet werden, und die Kontrolle gestellt werden, wo wir wollen es und seine center Eigenschaft wird zuverlässig sein);

  • jetzt, dass wir die center der Kontrolle in Frage haben, ersetzen Sie die führenden und Top-Einschränkungen mit NSLayoutAttributeCenterX und NSLayoutAttributeCenterY Zwänge, die Werte für center Eigenschaft mit der constant für die NSLayoutConstraint wie wie folgt einzustellen.

So:

// 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; 
} 

Die Einzelheiten der Implementierung variieren kann, je nachdem, wie Sie die Einschränkungen der Kontrolle definiert haben Sie (in meinem Fall skalieren möchten, führende und oben beruhten auf der Superview, der es einfacher machte), aber hoffentlich illustriert es die Lösung, diese Beschränkungen zu entfernen und neue basierend auf dem Zentrum hinzuzufügen.

Sie könnten, wenn Sie nicht durch die Suche nach der Einschränkung infrage gehen möchten, wie ich oben, ein IBOutlet für die Top-und führenden Bedingungen statt, was den Prozess erheblich vereinfacht definieren. Dieser Beispielcode stammt aus einem Projekt, in dem ich aus verschiedenen Gründen die IBOutlet für die NSLayoutConstraint Referenzen nicht verwenden konnte. Aber mit den IBOutlet Referenzen für die Einschränkungen ist definitiv ein einfacher Weg zu gehen (wenn Sie mit Auto-Layout bleiben).

Zum Beispiel, wenn Sie Interface Builder gehen, können Sie die Einschränkung in Frage und Steuer -Ziehen an den Assistenten-Editor markieren, um Ihren IBOutlet:

make constraint IBOutlet

Wenn Sie das tun, anstatt durch alle Bedingungen laufen, können Sie jetzt einfach sagen, zum Beispiel:

if (self.imageViewVerticalConstraint) 
{ 
    [self.view removeConstraint:self.imageViewVerticalConstraint]; 

    // create the new constraint here, like shown above 
} 

Ehrlich gesagt, ich Interface Builder die Möglichkeit wünschen t hatte o Einschränkungen wie diese sofort zu definieren (d. h. anstatt einer Einschränkung "Steuerung von links nach Superview", einer Constraint-Mitte "von Control nach links von Superview", aber ich denke nicht, dass es in IB möglich ist, also ändere ich meine Einschränkungen programmgesteuert. Aber indem ich diesen Prozess durchführe, kann ich jetzt die Steuerung skalieren und sie nicht aufgrund von Beschränkungen bewegen.


Wie 0x7fffffff erwähnt, wenn Sie eine CATransform3DMakeScale auf die Ebene anwenden, wird es nicht automatisch die Einschränkungen gelten, so dass Sie es nicht wie bewegen sehen, wenn Sie CGAffineTransformMakeScale auf die Ansicht anwenden. Aber wenn Sie etwas tun, um Einschränkungen erneut anzuwenden (setNeedsLayout oder irgendwelche Änderungen an irgendwelchen UIView Objekten können dazu führen, dass die Beschränkungen erneut angewendet werden), wird die Ansicht auf Sie weitergehen. Sie können sich also möglicherweise "einschmuggeln", wenn Sie die Transformation des Layers wieder in die Identität zurückversetzen, bevor die Constraints erneut angewendet werden. Am sichersten ist es jedoch, das automatische Layout auszuschalten oder die Einschränkungen zu beheben.

+0

Sehr umfassende Antwort! Vielen Dank! Ich werde etwas Zeit brauchen, um das Ganze zu verstehen, aber danke, dass du dir die Zeit genommen hast zu antworten! – JoshDG

+0

Sehr hepfull Antwort !!! Du hast meinen Tag gerettet ! – Amnysia

+0

Ich habe gerade die Top- und Leading-Constraints in Storyboard in Center-Constraints geändert, aber ein ähnliches Problem tritt immer noch auf. Statt nach links zu springen, bleibt die Subview nicht auf der rechten Seite der Superview (wo sie sein soll)), es bewegt sich gleichmäßig auf seine Position, aber während der Animation ist es immer in der falschen Position =/ –

Verwandte Themen