iOS:CGAffineTransformScale移动了我的物体

13

基于我之前提出的问题

尝试转换标签的简单按钮。 我想让它缩小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
你使用核心动画旋转和核心图形缩放有什么原因吗?我建议你也尝试在标签层上执行缩放,看看是否有帮助。 - Mick MacCallum
@0x7fffffff 我刚刚测试了一下:如果你对图层应用 CATransform3DMakeScale,它不会自动应用约束条件,所以你看不到它像对视图应用 CGAffineTransformMakeScale 那样移动。但是,如果你重新应用约束条件(setNeedsLayout 或者对任何 UIView 对象进行任何更改都可以导致约束条件被重新应用),视图就会移动。因此,如果在重新应用约束条件之前将图层的变换恢复为标识,你可能能够“偷懒”,但最好还是关闭自动布局或修复约束条件。 - Rob
1个回答

18

我假设您正在使用自动布局:在自动布局中,如果您有前导和/或顶部约束,则在使用 CGAffineTransformMakeScale 进行缩放后,前导/顶部约束将被重新应用,您的控件将移动以确保仍满足约束条件。

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

  • 等到 viewDidAppear(因为在 IB 中定义的约束将被应用,并且控件将放置在我们想要的位置,其 center 属性将是可靠的);

  • 现在,我们已经拥有了所讨论控件的 center,将前导和顶部约束替换为 NSLayoutAttributeCenterXNSLayoutAttributeCenterY 约束,使用 center 属性的值来设置 NSLayoutConstraintconstant

因此:

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

你的实现细节可能会因为控件约束的定义方式而有所不同(在我的例子中,leading和top是基于父视图的,这样做更容易),但希望它能说明解决方案,即移除那些约束并添加基于中心点的新约束。
如果你不想像我上面那样迭代查找相关约束,你可以为顶部和leading约束定义一个IBOutlet,这样可以大大简化流程。这个示例代码来自一个项目,由于各种原因,我不能使用NSLayoutConstraint引用的IBOutlet。但是,如果你坚持使用自动布局,使用IBOutlet引用约束肯定是更容易的方法。
例如,如果你去Interface Builder,你可以突出显示相关约束,然后使用control拖动到助手编辑器中,以创建你的IBOutlet: make constraint IBOutlet 如果你这样做了,就不需要遍历所有的约束了,你现在只需要说,例如:
if (self.imageViewVerticalConstraint)
{
    [self.view removeConstraint:self.imageViewVerticalConstraint];

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

坦白地说,我希望Interface Builder能够直接定义这样的约束(即不是“控件的前端到父视图左侧”的约束,而是“控件的中心到父视图左侧”的约束),但我认为这在IB中无法实现,因此我要通过编程方式修改我的约束。通过这个过程,我现在可以缩放控件而不会因为约束而移动。
正如0x7fffffff所指出的,如果你对层应用了一个`CATransform3DMakeScale`,它不会自动应用约束,因此你不会看到它像对视图应用`CGAffineTransformMakeScale`那样移动。但是,如果你重新应用了约束(`setNeedsLayout`或对任何`UIView`对象进行任何更改都会导致约束被重新应用),那么视图就会移动。因此,如果在重新应用约束之前将层的变换恢复为恒等变换,则可能可以“偷懒”,但最安全的方法可能是关闭自动布局或修复约束。

非常全面的回答!谢谢!我需要一些时间来理解整个过程,但是感谢您用心解答! - JoshDG
我刚刚在Storyboard中将顶部和前导约束更改为居中约束,但仍然出现类似的问题,期望子视图不是跳到左边,而是不粘附到父视图的右侧(它应该在那里),它均匀地移动到其位置,但在动画过程中始终处于错误的位置=/ - Rodrigo Ruiz
我有同样的问题。请注意,如果您正在调整所涉及的视图大小,则中心位置也会发生更改(因为宽度/高度发生了变化)。 - Borzh
没错,这就是为什么要使用centerX和centerY约束来确保变化的视图不会改变其位置。 - Rob

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接