layoutSubviews во время анимации?
У меня есть UIView
с кучей подпредставлений, все позиционируются с использованием layoutSubviews
, Когда view
изменяется, все относительные позиции меняются. Я хотел бы, чтобы эти пересчеты происходили во время анимированного изменения размера (используя +[UIView beginAnimations:]
звонки). Кажется, этого не происходит. Есть идеи?
4 ответа
Предположение: вы хотите иметь несколько шагов анимации (то есть положение не изменяется линейно с размером кадра).
Это невозможно с одной "стандартной" анимацией UIView. Зачем? Рамка / границы устанавливаются только один раз.
В CoreAnimation есть три "дерева слоев":
- Дерево моделей - это то, где ваше приложение думает о вещах.
- Дерево презентации - это примерно то, что отображается на экране.
- Дерево рендеринга - это примерно то, что CoreAnimation создает.
UIView - это (несколько тонкая) оболочка вокруг слоя модели. Во время анимации UIView CoreAnimation обновляет дерево представления / рендеринга - дерево модели представляет конечную точку анимации. В результате ваш код может (по большей части) обрабатывать анимацию как мгновенную - перемещение представления от A до B мгновенно перемещает его в B; Изменения просто оказываются анимированными для пользователя.
Есть более сложные вещи, которые вы можете сделать с помощью CALayer/CAAnimation напрямую, но я не очень много исследовал.
Вы можете связать несколько анимаций вместе, используя -[UIView setAnimationDidStopSelector:]. (Вы также можете попробовать использовать несколько анимаций вместе с setAnimationDelay:, но я не уверен, что происходит с несколькими анимациями в одном и том же свойстве; вам может повезти с setAnimationBeginsFromCurrentState:.)
Если вы хотите действительно детальное управление, CADisplayLink (OS 3.1+) - это таймер, который срабатывает после каждого обновления экрана. Резервным вариантом (для поддержки 3.0) является использование NSTimer с частотой 30/60 Гц или около того.
Я знаю, что это старый вопрос, но этот код работает для меня очень хорошо (подходит для вашего примера изменения фрейма).
-(void)layoutSubviews{
[super layoutSubviews];
// layout your subviews here, or whatever
}
-(void)someMethod{
double duration=...;
[UIView animateWithDuration:duration animations:^{
self.frame = ...;
[self layoutIfNeeded];
}];
}
Конечно, вы можете вызвать этот метод из другого объекта. "Хитрость" заключается в вызове layoutIfNeeded (или layoutSubviews напрямую - то же самое, если вы меняете фрейм, вызывается setNeedsLayout).
Как тс. Хорошо объяснив "деревья слоев", вы просто заставляете слой презентации отображать финальную стадию слоя модели с анимацией.
Преимущество этого метода заключается в возможности контролировать, когда изменение кадра / границ анимируется и когда оно происходит мгновенно.
Надеюсь, это поможет кому-то:).
Завершая ответ @GrizzlyNetch, вы можете установить UIViewAnimationOptionLayoutSubviews
опция анимации, поэтому вам не нужно звонить layoutIfNeeded
:
-(void)someMethod{
double duration = ...;
[UIView animateWithDuration:duration delay:0 options:UIViewAnimationOptionLayoutSubviews animations:^{
self.frame = ...;
} completion:nil];
}
Выкладываю для полноты. Благодаря тк. для объяснения того, что именно то, что я хочу сделать, не поддерживается Core Animation.
В конце концов я пришел к разумному решению. Вместо того, чтобы размещать мои подпредставления в -layoutSubviews, я делаю это в -setBounds:. Затем, когда я обертываю -setBounds: call в блоке UIView +beginAnimations:, эти позиционные вызовы также анимируются, и конечным результатом является то, что все правильно анимируется туда, где это должно быть богом.