尝试转换标签的简单按钮.我希望它减少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"); } }]; }
解决方法
>等待直到viewDidAppear(因为IB中定义的约束被应用,并且控件将被放置在我们想要的位置,并且其中心属性将是可靠的);
>现在我们有控件的中心,使用NSLayoutAttributeCenterX和NSLayoutAttributeCenterY约束替换主要和最重要的约束,使用中心属性的值来设置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,您可以突出显示所涉及的约束,并将控件拖到助手编辑器中,以使您的IBOutlet:
如果你这样做,而不是迭代所有的约束,你现在可以说,例如:
if (self.imageViewVerticalConstraint) { [self.view removeConstraint:self.imageViewVerticalConstraint]; // create the new constraint here,like shown above }
坦白说,我希望Interface Builder有能力立即开始定义这些约束(即,而不是“superview”左边的控制领先“约束,”superview“左边的控制中心)约束),但是我不要以为可以在IB中完成,所以我以编程方式改变了我的约束.但是,通过这个过程,我现在可以扩展控制,而不是因为约束而在我身上移动.
如0x7fffffff所示,如果将CATransform3DMakeScale应用于图层,则它不会自动应用约束,因此您将不会看到它像将CGAffineTransformMakeScale应用于视图一样移动.但是,如果您做任何事情来重新应用约束(setNeedsLayout或对任何UIView对象进行任何更改可能会导致重新应用约束),视图将在您的身上移动.因此,如果在重新应用约束之前将层的变换恢复为身份标识,则可能会“潜入”,但关闭自动布局或修复约束可能最为安全.