LayoutSubviews во время анимации?

У меня есть UIView с кучей subviews, все позиционируются с помощью layoutSubviews. Когда размер view изменяется, относительные позиции все изменяются. Я бы хотел, чтобы эти повторные вычисления выполнялись во время анимированного изменения размера (с использованием вызовов + [UIView beginAnimations:]). Кажется, это не происходит. Любые идеи?

Ответы

Ответ 1

Предположение: вы хотите иметь несколько шагов анимации (т.е. положение не изменяется линейно с размером кадра).

Это невозможно с помощью одной "стандартной" анимации UIView. Зачем? Рамка/граница устанавливается только один раз.

Core Animation имеет три "дерева слоев":

  • Дерево моделей - это то, о чем ваше приложение думает.
  • Дерево презентации примерно соответствует отображаемому на экране.
  • Дерево рендеринга примерно соответствует компоновке Core Animation.

UIView - (несколько тонкая) обертка вокруг слоя модели. Во время анимации UIView Core Animation обновляет дерево презентаций/рендеринга — дерево модели представляет конечную точку анимации. Результат заключается в том, что ваш код может (по большей части) обрабатывать анимацию как мгновенный — перемещение вида от A до B мгновенно перемещает его в B; изменение просто происходит для анимированного пользователя.

С CALayer/CAAnimation можно заниматься более сложными вещами, но я этого недостаточно изучил.

Вы можете связать несколько анимаций вместе, используя - [UIView setAnimationDidStopSelector:]. (Вы также можете попробовать использовать несколько анимаций вместе с setAnimationDelay:, но я не уверен, что происходит с несколькими анимациями в одном и том же свойстве: вам может повезло с setAnimationBeginsFromCurrentState:.)

Если вам нужен действительно тонкий контроль, CADisplayLink (OS 3.1+) - это таймер, который запускается после каждого обновления экрана. Резервным вариантом (для поддержки 3.0) является использование NSTimer при 30/60 Гц или около того.

Ответ 2

Я знаю, что это старый вопрос, но этот код работает для меня очень хорошо (подходит для вашего примера смены фрейма).

-(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).

Как tc. приятно объяснили "деревья слоев", вы просто заставляете слой презентации отображать заключительный этап слоя модели с анимацией.

Преимущество этого метода заключается в возможности управления, когда анимация кадра/границ анимируется и когда она мгновенно.

Надеюсь, это поможет кому-то:).

Ответ 3

Завершая @GrizzlyNetch anwer, вы можете установить опцию анимации UIViewAnimationOptionLayoutSubviews, поэтому вам не нужно вызывать layoutIfNeeded:

-(void)someMethod{
    double duration = ...;
    [UIView animateWithDuration:duration delay:0 options:UIViewAnimationOptionLayoutSubviews animations:^{
        self.frame = ...;
    } completion:nil];
}

Ответ 4

Публикация для полноты. Благодаря tc. для объяснения того, что я хочу сделать, точно, не поддерживается Core Animation.

В конце концов я придумал разумное решение. Вместо этого планируйте мои подпрограммы в -layoutSubviews, я делаю это в -setBounds:. Затем, когда я обертываю -setBounds: вызов в UIView + beginAnimations: block, эти позиционирующие вызовы также анимируются, и конечным результатом является все, что правильно оживляет, где оно должно быть богом.