2013-04-29 71 views
13

建立在我有earlier的问题上。iOS:CGAffineTransformScale移动我的对象

试图转换标签的简单按钮。我希望它缩小0.5倍,但是由于某种原因,它也会移动物体。标签向上跳到左侧,然后转换。

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

是否有一个原因,你为什么使用核心动画的旋转和核心图形的规模?我会尝试在标签的图层上执行比例,看看是否有帮助。 – 2013-04-30 00:19:44

+0

@ 0x7fffffff我刚刚测试过它:如果将'CATransform3DMakeScale'应用于图层,它将不会自动应用这些约束,因此,如果将'CGAffineTransformMakeScale'应用于视图,您将看不到它的移动。但是如果你做任何事情来重新应用约束('setNeedsLayout'或者对任何'UIView'对象做任何修改都会导致约束被重新应用),那么视图就会移动。因此,如果您在重新应用约束之前将图层的变换恢复为身份,则可能会“偷偷摸摸”,但关闭自动布局或仅修复约束可能最安全。 – Rob 2013-04-30 03:03:38

回答

18

我从您使用自动布局问题假设:在自动布局,如果你有一个领先的和/或顶部约束,你CGAffineTransformMakeScale规模后,领导/顶部约束会重新申请,你的控制权将移动你,以确保约束仍然满足。

您可以关闭自动布局(这是最简单的答案),或者你可以:

  • 等到viewDidAppear(因为被应用在IB定义的约束,以及控制将被放置在这里我们要它和它的center财产将是可靠的);

  • 现在我们已经有问题的控制center,与NSLayoutAttributeCenterXNSLayoutAttributeCenterY约束更换领导和主要约束,使用值center属性设置constantNSLayoutConstraint为如下。

这样:

// 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中,您可以突出显示有问题的约束和控制 -drag的助理编辑,让您的IBOutlet

make constraint IBOutlet

如果你这样做,而不是通过所有的约束迭代,你现在就可以说,例如:

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

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

坦率地说,我希望界面生成器有能力ŧ o直接定义这些约束条件(即而不是一个“超级控制左边的控制引导”约束,一个“超级控制左边的控制中心”约束),但我认为它不能在IB中完成,所以我正在以编程方式改变我的约束。但通过这个过程,我现在可以扩大控制范围,并且不会因为限制而在我身边移动。


正如指出的为0x7FFFFFFF,如果你申请一个CATransform3DMakeScale到层,它不会自动应用的限制,所以你不会看到它移到如果你申请CGAffineTransformMakeScale到视图等。但是,如果您执行任何操作来重新应用约束条件(setNeedsLayout或对任何UIView对象所做的任何更改都可能导致约束被重新应用),则该视图将随您移动。因此,如果您在重新应用约束之前将图层的变换恢复为身份,则可能会“偷偷摸摸”,但关闭自动布局或仅修复约束可能最安全。

+0

非常全面的答案!谢谢!我会花一些时间来了解整个事情,但谢谢你花时间回答! – JoshDG 2013-04-30 19:40:19

+0

非常hepfull答案!你救了我的一天! – Amnysia 2013-11-07 17:31:39

+0

我刚刚在故事板中改变了顶端和主要限制条件,但类似的问题仍然存在,预计子视图不会跳到左侧,而是不会粘到超级视图的右侧(它应该是),它会一致地移动到它的位置,但在动画期间它总是处于错误的位置=/ – 2014-03-23 06:01:35