2013-04-29 6 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

Есть ли причина, по которой вы используете базовую анимацию для вращения и основную графику для шкалы? Я бы попытался выполнить масштаб на слое ярлыка, а также посмотреть, помогает ли это. –

+0

@ 0x7fffffff Я только что протестировал его: если вы примените к слою «CATransform3DMakeScale», он не будет автоматически применять ограничения, поэтому вы не увидите его перемещение, как если бы вы применили 'CGAffineTransformMakeScale' к представлению. Но если вы сделаете что-либо, чтобы повторно использовать ограничения ('setNeedsLayout' или любые изменения каких-либо объектов' UIView' могут привести к повторному использованию ограничений), представление будет перемещаться по вам. Таким образом, вы можете «подкрасться» в том случае, если вы восстановите преобразование слоя обратно в личность до того, как будут переназначены ограничения, но, вероятно, безопаснее отключить автозапуск или просто исправить ограничения. – Rob

ответ

18

я предполагаю, от вопроса, который вы используете автоматическую раскладку: В автоматическом макете, если у вас есть ведущие и/или верхнее ограничение, после масштабирования с CGAffineTransformMakeScale, ведущим/топ ограничением будет повторно и ваш контроль будет двигаться на вас, чтобы гарантировать, что ограничение все еще выполняется.

Вы можете отключить автоматическую раскладку (который является простой ответ), или вы можете:

  • ждать до viewDidAppear (поскольку ограничения, определенные в IB применяются, а управление будет размещен, где мы не хотим это и его имущество center будет надежным);

  • теперь, когда мы имеем center элемента управления в вопросе, замените ведущий и верхние ограничения с NSLayoutAttributeCenterX и NSLayoutAttributeCenterY ограничений, используя значения для center имущества установить constant для 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. Но использование ссылок для ограничений - это, безусловно, более простой способ (если вы придерживаетесь автоматического макета).

Например, если вы идете в Interface Builder, вы можете выделить ограничение в вопросе и управления к Drag-помощника редактора, чтобы сделать ваш IBOutlet:

make constraint IBOutlet

Если вы сделаете это, а не перебор всех ограничений, теперь вы можете просто сказать, например:

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

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

Честно говоря, я хотел бы Interface Builder имел возможность т o определять ограничения, подобные этим, прямо из коробки (т. а не «ограничение управления слева от супервизора», ограничение «центра управления слева от супервизора»), но я не думаю, что это можно сделать в IB, поэтому я программно изменяю свои ограничения. Но, пройдя этот процесс, я могу теперь масштабировать элемент управления и не перемещать его по мне из-за ограничений.


Как 0x7fffffff отметил, если применить CATransform3DMakeScale к слою, он не будет автоматически применять ограничения, поэтому вы не увидите его двигаться, как если применить CGAffineTransformMakeScale к виду. Но если вы сделаете что-либо для повторного применения ограничений (setNeedsLayout или внесите какие-либо изменения в любые объекты UIView, это может привести к повторному использованию ограничений), представление будет перемещаться по вам. Таким образом, вы можете «подкрасться» в том случае, если вы восстановите преобразование слоя обратно в личность до того, как будут переназначены ограничения, но, вероятно, безопаснее отключить автозапуск или просто исправить ограничения.

+0

Очень полный ответ! Благодаря! Понадобится время, чтобы понять все, но спасибо, что нашли время ответить! – JoshDG

+0

Очень hepfull ответ !!! Ты спас мой день! – Amnysia

+0

Я только что сменил верхние и ведущие ограничения на центральные ограничения в раскадровке, но аналогичная проблема все еще случается, ожидайте, что вместо того, чтобы прыгать влево, subview просто не придерживается права супервизора (где предполагается, что), он равномерно перемещается в свое положение, но во время анимации он всегда находится в неправильном положении =/ –

Смежные вопросы