Анимировать изменения intrinsicContentSize

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

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

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

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

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

6 ответов


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

[UIView animateWithDuration:0.2 animations:^{
    [self invalidateIntrinsicContentSize];
    [self.superview setNeedsLayout];
    [self.superview layoutIfNeeded];
}];

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

[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 и сделайте это внутри этого метода. Разные способы. ..


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

[UIView animateWithDuration:.2 animations:^{
        self.intrinsicClass.numberOfWeeks=8;
        [self.intrinsicClass setNeedsLayout];
        [self.intrinsicClass layoutIfNeeded];
    }];

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

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

и ряд вариаций. Никто не работает. Ярлык немедленно изменяет его размер и после этого сползает в его новое положение. Таким образом, анимация экрана позиции надписей ons работает. Но анимация изменения размера этикетки-нет.


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

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

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

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

нормальное Я.вид.layoutIfNeeded () будет работать большую часть времени.