2013-11-11 2 views
5

Давайте предположим, что у меня есть этот UIView:AutoLayout, ограничения и анимация

enter image description here

с этими относительными ограничениями:

@property (strong, nonatomic) IBOutlet NSLayoutConstraint *leftMarginConstraint; 
@property (strong, nonatomic) IBOutlet NSLayoutConstraint *topMarginConstraint; 
@property (strong, nonatomic) IBOutlet NSLayoutConstraint *widthConstraint; 
@property (strong, nonatomic) IBOutlet NSLayoutConstraint *heightConstraint; 

Хорошо, теперь давайте предположим, что, когда пользователь нажимает на эту UIButton в кнопка должна перейти в нижний правый угол представления. Мы можем легко использовать два ограничения, определяющие нижнее пространство между кнопкой и нижней маской руководства и правое пространство (конечное пространство) от кнопки и правого края представления.

Проблема заключается в том, что у UIButton уже были два ограничения (слева/сверху) и два ограничения, определяющие его ширину и высоту, поэтому мы не можем добавить два новых ограничения, потому что они будут конфликтовать с другими.

Простая и распространенная ситуация для сценария анимации, но это вызывает у меня некоторые проблемы. Идеи?

EDIT

Когда пользователь нажимает на UIButton, мне нужно, чтобы кнопка:

  1. изменить свое название на "вторую"
  2. ждать 1 секунду и перейти к снизу- правый угол (удалите ограничения верхнего и левого полей и добавьте ограничения нижнего и левого полей)
  3. изменить название на «Третий»
  4. подождать 1 секунду и перейти в верхнем правом углу (удаление нижнего поля ограничения и добавить верхнее поле ограничения)

ли я серьезно обязан использовать этот грязный код?

@implementation ViewController 
{ 
    NSLayoutConstraint *_topMarginConstraint; 
    NSLayoutConstraint *_leftMarginConstraint; 
    NSLayoutConstraint *_bottomMarginConstraint; 
    NSLayoutConstraint *_rightMarginConstraint; 
} 

- (IBAction)buttonPressed:(id)sender 
{ 
    UIButton *button = sender; 

    // 1. 
    [sender setTitle:@"Second" forState:UIControlStateNormal]; 

    // 2. 
    double delayInSeconds = 1.0; 
    dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC); 
    dispatch_after(popTime, dispatch_get_main_queue(), ^(void) { 
     [button removeConstraints:@[self.leftMarginConstraint, self.topMarginConstraint]]; 
     _bottomMarginConstraint = [NSLayoutConstraint constraintWithItem:self.view 
                   attribute:NSLayoutAttributeBottom 
                   relatedBy:0 toItem:button 
                   attribute:NSLayoutAttributeBottom 
                   multiplier:1 
                   constant:20]; 
     [self.view addConstraint:_bottomMarginConstraint]; 

     _rightMarginConstraint = [NSLayoutConstraint constraintWithItem:self.view 
                   attribute:NSLayoutAttributeRight 
                   relatedBy:0 toItem:button 
                   attribute:NSLayoutAttributeRight 
                  multiplier:1 
                   constant:20]; 
     [self.view addConstraint:_rightMarginConstraint]; 

     [UIView animateWithDuration:1 animations:^{ 
      [self.view layoutIfNeeded]; 
     } completion:^(BOOL finished) { 
      // 3. 
      [sender setTitle:@"Third" forState:UIControlStateNormal]; 

      // 4. 
      double delayInSeconds = 1.0; 
      dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC); 
      dispatch_after(popTime, dispatch_get_main_queue(), ^(void) { 
       [button removeConstraint:_bottomMarginConstraint]; 
       _topMarginConstraint = [NSLayoutConstraint constraintWithItem:self.view 
                     attribute:NSLayoutAttributeTop 
                     relatedBy:0 toItem:button 
                     attribute:NSLayoutAttributeTop 
                     multiplier:1 
                     constant:20]; 
       [UIView animateWithDuration:1 animations:^{ 
        [self.view layoutIfNeeded]; 
       }]; 
      }); 
     }]; 
    }); 
} 

Серьезный? : D

+0

Есть ли в любом случае избежать раздражающего исключения 'UIViewAlertForUnsatisfiableConstraints:', которое происходит между удалением и добавлением конгруэнтных ограничений? – Glavid

ответ

10

Удалите левые и верхние ограничения, добавьте нижнее и правое ограничения, затем вызовите layoutIfNeeded в блоке анимации, чтобы анимировать новую позицию. Код во втором методе move2 может быть встроен в блок завершения move1, но мне становится легче читать, если вы сохраняете два перемещения в отдельных методах (это требует добавления другого свойства, bottomCon):

- (IBAction)move1:(UIButton *)sender { 
    [sender setTitle:@"Second" forState:UIControlStateNormal]; 
    [sender layoutIfNeeded]; 
    [self.view removeConstraints:@[self.leftCon,self.topCon]]; 
    self.bottomCon = [NSLayoutConstraint constraintWithItem:self.view attribute:NSLayoutAttributeBottom relatedBy:0 toItem:self.button attribute:NSLayoutAttributeBottom multiplier:1 constant:20]; 
    [self.view addConstraint:self.bottomCon]; 
    [self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.view attribute:NSLayoutAttributeRight relatedBy:0 toItem:self.button attribute:NSLayoutAttributeRight multiplier:1 constant:20]]; 
    [UIView animateWithDuration:1 delay:1.0 options:0 animations:^{ 
     [self.view layoutIfNeeded]; 
    } completion:^(BOOL finished) { 
     [sender setTitle:@"Third" forState:UIControlStateNormal]; 
     [self move2]; 
    }]; 
} 

-(void)move2 { 
    [self.view removeConstraint:self.bottomCon]; 
    [self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.view attribute:NSLayoutAttributeTop relatedBy:0 toItem:self.button attribute:NSLayoutAttributeTop multiplier:1 constant:-20]]; 
    [UIView animateWithDuration:1 delay:1.0 options:0 animations:^{ 
     [self.view layoutIfNeeded]; 
    } completion:nil]; 
} 
+0

Спасибо @rdelmar, я обновил свой вопрос. –

+0

@FredCollins, ну, похоже, вы изменили свой вопрос. Существуют и другие способы сделать это с помощью animateWithDuration: анимации: завершение: это не потребует использования dispatch_after, но я не знаю, будет ли это менее «грязным». – rdelmar

+0

@FredCollins, я обновил свой ответ, чтобы показать, как я это сделаю - я думаю, что читать и понимать это проще. – rdelmar

1

Для анимации вам нужно изменить константу ограничения слева и сверху вместо добавления нового ограничения. В следующем порядке:

self.mBtnTopConstraint.constant = yourval1; 
    self.mBtmLeftConstraint.constant = yourval2; 
    [yourbtn setNeedsUpdateConstraints]; 

    [UIView animateWithDuration:0.5 animations:^{ 

      [yourbtn layoutIfNeeded]; 

     } completion:^(BOOL isFinished){ 
     }]; 

Надеюсь, что это поможет.

+0

Все, что предложил rdelmar, также является одним из способов сделать это. –

+0

Да, на самом деле это лучше, потому что мне нужно избавиться от некоторых ограничений объявлений в разное время. (Проверить вопрос об обновлении) –

-1

Вы можете сделать UIView-заполнитель с искомыми ограничениями и заменить первоначальные ограничения с помощью ограничений UIView-заполнителя.

Таким образом, он сохраняет ограничения компоновки в раскадровке и не дает вашему коду быть беспорядочным. Он также дает вам дополнительный эффект от просмотра конечного результата вашей анимации в разделе предварительного просмотра xcode.

Если вы используете cocoapod «SBP», чтобы сделать это всего несколькими парами. Here's a tutorial.

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