Подтвердить что ты не робот

Анимация внутренних измененийContentSize

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

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

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

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

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

4b9b3361

Ответ 1

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

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

Ответ 2

Ограничение ширины/высоты не помогает? Продолжайте ссылаться на это ограничение и...

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

Ответ 3

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

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

Ответ 4

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

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

И ряд вариантов. Ничего не работает. Ярлык сразу же изменяет его размер, а затем переходит в новую позицию. Таким образом, анимация экрана наложения этикеток работает. Но анимация изменения размера метки не такова.

Ответ 5

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

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

Ответ 6

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

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

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