Анимация изменений innerContentSize

У меня есть подкласс UIView, который рисует круг, радиус которого изменяется (с хорошей анимацией). Вид определяет размер круга.

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

Я понимаю, что реализация -(CGSize)intrinsicContentSize и вызов invalidateIntrinsicContentSize при изменении радиуса сообщат ограничениям об обновлении, но я не могу понять, как анимировать изменения в intrinsicContentSize.

Вызов invalidateIntrinsicContentSize из блока [UIView animateWith... просто мгновенно обновляет макет.

Возможно ли это вообще, и есть ли обходной/лучший подход?


person Loz    schedule 17.06.2013    source источник
comment
У меня ощущение, что это невозможно. Возьмем пример UILabel, его intrinsicContentSize зависит от размера шрифта. Однако нет хорошего способа анимировать изменение шрифта. Обходной путь для метки - анимировать изменение преобразования ее слоя (используя CAAnimation), а затем изменить шрифт после завершения анимации - нехорошо.   -  person Robert    schedule 16.08.2013


Ответы (6)


invalidateIntrinsicContentSize хорошо работает с анимацией и layoutIfNeeded. Единственное, что вам нужно учитывать, это то, что изменение размера внутреннего содержимого делает недействительным макет суперпредставления. Итак, это должно работать:

[UIView animateWithDuration:0.2 animations:^{
    [self invalidateIntrinsicContentSize];
    [self.superview setNeedsLayout];
    [self.superview layoutIfNeeded];
}];
person stigi    schedule 16.07.2014
comment
Хорошо, работает, если вы добавите строку [self.superview setNeedsLayout] посередине - person Ash; 07.10.2014
comment
Обновлено с учетом отзывов @Ash. - person stigi; 17.09.2016

Быстрая версия ответа @stigi, которая сработала для меня:

UIView.animate(withDuration: 0.2, animations: {
    self.invalidateIntrinsicContentSize()
    self.superview?.setNeedsLayout()
    self.superview?.layoutIfNeeded()
})
person nathangitter    schedule 04.08.2017

Ограничение ширины/высоты не помогает? Сохраните ссылку на это ограничение и...

[NSLayoutConstraint constraintWithItem:view
                             attribute:NSLayoutAttributeWidth
                             relatedBy:NSLayoutRelationEqual
                                toItem:nil
                             attribute:NSLayoutAttributeNotAnAttribute
                            multiplier:1
                              constant:myViewInitialWidth];

... когда вы хотите анимировать изменение размера myView, сделайте это ...

self.viewWidthConstraint.constant = 100; // new width
[UIView animateWithDuration:0.3 animations:^{ [view layoutIfNeeded]; }];

... сделайте то же самое для высоты.

В зависимости от ваших других ограничений, возможно, вам придется повысить приоритет этих двух ограничений.

Или вы можете создать подкласс UIView, добавить - (void)invalidateIntrinsicContentSize:(BOOL)animated и подделать его самостоятельно. Получите новый размер из - (CGSize)intrinsicContentSize и анимируйте его, анимировав ограничения ширины/высоты. Или добавьте свойство для включения/отключения анимации и переопределения invalidateIntrinsicContentSize и сделайте это внутри этого метода. Много способов ...

person zrzka    schedule 10.09.2013

В приведенном ниже коде внутренний класс — это класс, который только что изменил свой размер при изменении переменной. Для анимации встроенного класса используйте только приведенный ниже код. Если это влияет на другие объекты выше в иерархии представлений, замените класс self.intrinsic представлением верхнего уровня для setNeedsLayout и layoutIfNeeded.

[UIView animateWithDuration:.2 animations:^{
        self.intrinsicClass.numberOfWeeks=8;
        [self.intrinsicClass setNeedsLayout];
        [self.intrinsicClass layoutIfNeeded];
    }];
person Ajaxharg    schedule 23.06.2014
comment
Это особенно верно для UIViews с intrinsicContentSize внутри UIStackView. Чтобы представление стека анимировалось при изменении размера, вы должны вызвать layoutIfNeeded для родителя стека, а не для самого стека. Я просто укусил это! - person Dafydd Williams; 19.12.2018

Ничто из этого не сработало для меня. У меня есть UILabel, который я устанавливаю с помощью NSAttributedString. Текст многострочный и обтекает границы слов. Поэтому высота переменная. Я пробовал это:

[UIView animateWithDuration:1.0f animations:^{
    self.label = newLabelText;
    [self.label invalidateIntrinsicContentSize];
    [self.view setNeedsLayout];
    [self.view layoutIfNeeded];
}];

И ряд вариаций. Ни один не работает. Метка немедленно меняет свой размер, а затем перемещается в новое положение. Так что анимация положения меток на экране работает. А вот анимация изменения размера метки - нет.

person drekka    schedule 05.05.2015
comment
Это может быть комментарий, потому что он не дает ответа. - person Erik Kerber; 27.09.2019

Что ж, для Swift 4/3 это работает, и я думаю, что это лучшая практика. Если у вас есть UIView с UILabel, и UIView адаптирует кадр из UILabel, используйте это:

self.theUILabel.text = "text update"
UIView.animate(withDuration: 5.0, animations: {
   self.theUIView.layoutIfNeeded()
})

Обычный self.view.layoutIfNeeded() также будет работать большую часть времени.

person J. Doe    schedule 18.08.2017