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, он уже будет членом иерархии представлений, соответствующим образом связанным с ограничениями.
Как вы это сделаете? Любой вход очень ценится.
спасибо за ввод. Этот первый вызов 'layoutIfNeeded' был недостающим ключом к моей проблеме. Интересно, что сейчас так много смысла, что вы его показали, но мне это никогда не приходило в голову. Еще раз спасибо! – kadam
Вы правы в том, что подход 'objectAtIndex:' небезопасен. Однако визуальный формат передает идею лучше, поэтому, для примера, это послужило моим целям лучше. Однако я заменю визуальный формат в версии для доставки. – kadam