2015-01-13 4 views
2

Setup: есть containerView на экране внутри UIViewController (для простоты скажем containerView занимает весь экран).Как анимировать addSubView: с помощью Autolayout

Проблема: создать и добавить overlayView как подвид в containerView и анимировать появление так, что она будет анимировать в положение с правой стороны в ответ на действия пользователя.

UIView *overlayView = [[UIView alloc] initWithFrame:containerView.frame]; 
overlayView.translatesAutoresizingMaskIntoConstraints = NO; 
[containerView addSubview:overlayView]; // Can't add constraints before inserting into view hierarchy 

подход: связать передний край overlayView к переднему краю containerView с ограничением, что я буду называть overlayLeadingConstraint. Установите overlayLeadingConstraint.constant на ширину containerView так, чтобы он был первоначально расположен сразу за экраном справа.

NSArray *constraints = [NSLayoutConstraint constraintsWithVisualFormat:@"|[overlayView(width)]" options:0 metrics:metrics views:views]; 
[containerView addConstraints:constraints]; 
NSLayoutConstraint *overlayViewLeadingConstraint = [constraints objectAtIndex:0]; 
overlayViewLeadingConstraint.constant = containerView.frame.size.width; 
// Height constraint not shown for simplicity 

Анимация: Теперь на реальную проблему; анимировать overlayView в положение справа. Первый подход заключается в чем-то вроде этого:

overlayViewLeadingConstraint.constant = 0.0; 
[UIView animateWithDuration:1.0 animations:^{ 
    [containerView layoutIfNeeded]; 
}]; 

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

Второй подход: попытайтесь отложить анимацию до следующего цикла запуска до тех пор, пока начальный макет после addSubview: уже не состоялся.

dispatch_async(dispatch_get_main_queue(), ^{ 
    overlayViewLeadingConstraint.constant = 0.0; 
    [UIView animateWithDuration:1.0 animations:^{ 
     [containerView layoutIfNeeded]; 
    }]; 
}); 

Это не работает либо и намекает, что addSubview: и установка ограничений может занять несколько циклов прогонов.

Третий подход: попытайтесь отложить анимацию еще дальше: несколько циклов запуска в будущее.

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 
    overlayViewLeadingConstraint.constant = 0.0; 
    [UIView animateWithDuration:1.0 animations:^{ 
     [containerView layoutIfNeeded]; 
    }]; 
}]; 

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

Вопросы: выше подход, как представляется, обходной путь, а не реального решение. Поэтому мне интересно, есть ли лучший подход к этому. Я думал об использовании хостинговой viewController «S viewDidLayoutSubviews метода, чтобы узнать, когда overlayView находится в месте, и это нормально, чтобы запустить анимацию, но в документации явно советует против этого:

Однако этот метод вызывается не указывает, что индивидуальные макеты подсмотров представления были скорректированы. Каждое подчинение отвечает за настройку .

Я начинаю думать, что идея Apple заключалась в том, чтобы все вспомогательные элементы были добавлены во время инициализации и просто спрятали те, которые вам не нужны немедленно. Так что, когда придет время оживить subView, он уже будет членом иерархии представлений, соответствующим образом связанным с ограничениями.

Как вы это сделаете? Любой вход очень ценится.

ответ

4

Я не уверен, что вы делаете с некоторыми из вещей, которые вы не показывают, но этот код работал нормально для меня. Я сделал вид в IB (containerView), что составляет 200 х 200, и использовать этот код,

- (void)viewDidAppear:(BOOL)animated { 
    [super viewDidAppear:animated]; 
    UIView *overlayView = [UIView new]; 
    overlayView.backgroundColor = [UIColor redColor]; 
    overlayView.translatesAutoresizingMaskIntoConstraints = NO; 
    [containerView addSubview:overlayView]; 
    NSArray *constraints = [NSLayoutConstraint constraintsWithVisualFormat:@"|[overlayView]|" options:0 metrics:nil views:@{@"overlayView":overlayView}]; 
    NSArray *constraints2 = [NSLayoutConstraint constraintsWithVisualFormat:@"V:|[overlayView]|" options:0 metrics:nil views:@{@"overlayView":overlayView}]; 
    [containerView addConstraints:constraints]; 
    [containerView addConstraints:constraints2]; 
    NSLayoutConstraint *overlayViewLeadingConstraint = [constraints objectAtIndex:0]; 
    overlayViewLeadingConstraint.constant = containerView.frame.size.width; 
    [containerView layoutIfNeeded]; 

    overlayViewLeadingConstraint.constant = 0.0; 
    [UIView animateWithDuration:1.0 animations:^{ 
     [containerView layoutIfNeeded]; 
    }]; 
} 

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

Не знаю, можно ли использовать [constraints objectAtIndex:0], чтобы получить ограничение, которое вы хотите изменить. он работал в этом случае, но я не знаю, гарантирован ли порядок ограничений, установленных с визуальным форматом.

+0

спасибо за ввод. Этот первый вызов 'layoutIfNeeded' был недостающим ключом к моей проблеме. Интересно, что сейчас так много смысла, что вы его показали, но мне это никогда не приходило в голову. Еще раз спасибо! – kadam

+0

Вы правы в том, что подход 'objectAtIndex:' небезопасен. Однако визуальный формат передает идею лучше, поэтому, для примера, это послужило моим целям лучше. Однако я заменю визуальный формат в версии для доставки. – kadam

1

Вы ничего не добавили к блоку animations, который будет анимировать. Блок animations обычно считается «я хочу, чтобы мой пользовательский интерфейс оказался в этом состоянии, анимированный материал, чтобы сделать это».

[UIView animateWithDuration:1.0 animations:^{ 
    overlayViewLeadingConstraint.constant = 0.0; 
}]; 
+1

Спасибо за ввод, хотя я считаю, что рекомендуемый способ состоит в том, чтобы сначала изменить ограничение, а не в блоке анимации просто вызвать 'layoutIfNeeded'. Помещение постоянного обновления внутри блока анимации будет иметь тот же эффект, но вызов 'layoutIfNeeded' - это то, что запускает анимацию и поэтому отсутствует в вашем примере. – kadam

+0

Вы ошиблись. Как я уже сказал, если вы не выполняете изменения значений в блоке 'animations', они не будут анимированы. –

+1

Нет, Ян, вы ошибаетесь. Я сделал много анимаций, помещая изменение ограничения вне блока анимации (но внутри того же метода, который имеет вызов animateWithDuration), и он отлично работает (и вам нужно вызвать layoutIfNeeded внутри блока анимации, как указывал кадам). – rdelmar

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