2013-06-17 10 views
36

Mam podklasę UIView, która rysuje okrąg, którego promień się zmienia (z ładnymi animacjami skaczącymi). Widok decyduje o wielkości koła.Animate intrinsicContentSize changes

Chcę, aby ta podklasa UIView zmieniła rozmiar ramki, aby dopasować animowane zmiany do promienia okręgu, i chcę, aby te zmiany zmodyfikowały wszelkie NSLayoutConstraints połączone z widokiem (tak, aby widoki były ograniczone do krawędzi koła będzie się poruszać w miarę zmiany rozmiaru koła).

Rozumiem, że wykonanie -(CGSize)intrinsicContentSize i wywołanie invalidateIntrinsicContentSize, gdy zmiany promienia poinformuje wiązań do aktualizacji, ale nie mogę dowiedzieć się, jak animować zmiany do intrinsicContentSize.

Wywołanie invalidateIntrinsicContentSize od wewnątrz [UIView animateWith ... blok tylko natychmiast aktualizuje układ.

Czy to możliwe, i czy istnieje obejście/lepsze podejście?

+0

Mam przeczucie, że nie jest to możliwe. Weź przykład z 'UILabel' jego' intrinsicContentSize' jest funkcją rozmiaru czcionki. Jednak nie ma dobrego sposobu na animowanie zmiany czcionki. Obejściem problemu dla etykiety jest animacja zmiany transformacji jej warstwy (przy użyciu "CAAnimation"), a następnie zmiana czcionki po zakończeniu animacji - Niezła. – Robert

Odpowiedz

44

invalidateIntrinsicContentSize dobrze współpracuje z animacjami i layoutIfNeeded. Jedyną rzeczą, którą należy wziąć pod uwagę, jest to, że zmiana nieodłącznego rozmiaru treści unieważnia układ superview. To powinno zadziałać:

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

Proszę oznaczyć to jako odpowiedź. – rplankenhorn

+0

Nie działa dla mnie. – Ash

+9

OK, działa po dodaniu wiersza [self.superview setNeedsLayout] w środku – Ash

3

Ograniczenie szerokości do wysokości nie pomaga? Zachować odniesienie tego przymusu i ...

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

... kiedy chcemy animować myView rozmiaru, to zrobić ...

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

... zrobić to samo dla wysokości .

W zależności od innych ograniczeń, być może będziesz zmuszony podnieść priorytet tych dwóch ograniczeń.

Albo możesz podklasę UIView, dodać - (void)invalidateIntrinsicContentSize:(BOOL)animated i sfałszować ją samodzielnie. Pobierz nowy rozmiar z - (CGSize)intrinsicContentSize i animuj go, animując ograniczenia szerokości/wysokości. Lub dodaj właściwość, aby włączyć/wyłączyć animacje i zastąpić invalidateIntrinsicContentSize i zrobić to wewnątrz tej metody. Wiele sposobów ...

1

W poniższym kodzie klasa wewnętrzna jest klasą, która właśnie zmieniła swój rozmiar podczas zmiany zmiennej. Aby animować klasę wewnętrzną, użyj tylko poniższego kodu. Jeśli ma wpływ na inne obiekty znajdujące się wyżej w hierarchii widoku, należy zastąpić klasę self.intrinsic widokiem najwyższego poziomu dla setNeedsLayout i layoutIfNeeded.

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

Nic z tego nie zadziałało. Mam UILabel, który ustawiam z NSAttributedString. Tekst jest wielowierszowy i zawija się na granicach słów. Dlatego wysokość jest zmienna. Próbowałem tego:

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

I wiele odmian. Brak pracy. Etykieta natychmiast zmienia jej rozmiar, a następnie wsuwa się w nową pozycję. Tak działa animacja ekranu z pozycjami na etykietach. Ale animacja zmiany rozmiaru etykiety nie jest.

1

Swift wersja odpowiedzi @ stigi, który pracował dla mnie:

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

Dobrze dla Swift 4/3 to działa i myślę, że jest to najlepsza praktyka. Jeśli masz UIView z UILabel w nim i UIView dopasowuje ramkę z UILabel, użyj tego:

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

Normalna self.view.layoutIfNeeded() będzie pracować przez większość czasu, jak również.