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:, эти позиционные вызовы также анимируются, и конечным результатом является то, что все правильно анимируется туда, где это должно быть богом.

Другие вопросы по тегам