iOS: Wieloliniowy UILabel w układzie automatycznym

183

Mam problem z uzyskaniem bardzo podstawowego zachowania układu za pomocą funkcji automatycznego układu. Mój kontroler widoku wygląda tak w IB:

wprowadź opis zdjęcia tutaj

Górna etykieta to etykieta tytułowa, nie wiem ile to będzie linii. Potrzebuję etykiety tytułu, aby wyświetlić wszystkie wiersze tekstu. Potrzebuję również pozostałych dwóch etykiet i małego obrazu, który powinien być umieszczony tuż pod tytułem, jakkolwiek by nie był wysoki. Ustawiłem ograniczenia pionowego odstępu między etykietami a małym obrazem, a także górne ograniczenie odstępów między etykietą tytułu a jego widokiem oraz dolne ograniczenie między małym obrazem a widokiem. Biały UIView nie ma ograniczenia wysokości, więc powinien rozciągać się w pionie, aby zawierał swoje widoki podrzędne. Ustawiłem liczbę linii dla etykiety tytułu na 0.

Jak mogę zmienić rozmiar etykiety tytułu, aby dopasować ją do liczby linii wymaganych przez ciąg? Rozumiem, że nie mogę korzystać z metod setFrame, ponieważ korzystam z automatycznego układu. I muszę użyć Auto Layout, ponieważ potrzebuję tych innych widoków, aby pozostały poniżej etykiety tytułu (stąd ograniczenia).

Jak mogę to zrobić?

James Harpe
źródło
3
Walczę też z podobnym problemem. Ale wciąż mam problemy z uzyskaniem, aby górna etykieta dynamicznie dostosowywała wysokość do zawartości. Jak to osiągnąłeś?
jbandi
1
Proszę rozważyć zaznaczenie odpowiedzi @ mwhuss jako zaakceptowanej.
jemmons
1
Czy osiągnąłeś wymagany wynik?
Aniruddha,
sprawdź to, nie musisz dodawać pojedynczej linii kodu stackoverflow.com/a/36862795/4910767
Badal Shah

Odpowiedzi:

254

Użyj -setPreferredMaxLayoutWidthna UILabeli autoukład powinien obsłużyć resztę.

[label setPreferredMaxLayoutWidth:200.0];

Zobacz dokumentację UILabel na preferMaxLayoutWidth .

Aktualizacja:

Wystarczy ustawić heightograniczenie w serii ujęć na Greater than or equal to, nie trzeba ustawiaćPreferredMaxLayoutWidth.

mwhuss
źródło
8
Fajnie, zresztą zrobić to w kreatorze interfejsów?
Sjoerd Perfors,
12
Ustaw
10
Przypomnienie: pamiętaj, aby ustawić właściwość numberOfLines.
Li Fumin
2
Tak, użyj dowolnej szerokości, jaką chcesz.
mwhuss,
4
Zobacz stackoverflow.com/a/29180323/1529675, aby dowiedzieć się, jak ustalić preferredMaxLayoutWidthi zobaczyć devetc.org/code/2014/07/07/auto-layout-and-views-that-wrap.html, aby uzyskać krótki, ale pouczający opis, wraz z animowane pliki GIF (nie mój opis, znalazłem to przez Google)
tboyce12
213

Rozwiń liczbę wierszy zestawu etykiet do 0, a także, co ważniejsze, wysokość zestawu automatycznego układu do>> x. Automatyczny układ zajmie się resztą. Możesz również zawierać inne elementy oparte na poprzednim elemencie, aby prawidłowo ustawić.

automatyczny układ

Charlie Wu
źródło
Jeśli ta metoda nie działa, zobacz odpowiedź Cory Imdieke na temat upewnienia się, że kolejne widoki nie zabraniają automatycznemu układowi zmiany rozmiaru (ewentualnie) widoku wielowierszowego.
Tom Howard
1
To jedyna odpowiedź, która działała dla mnie w komórce widoku tabeli. Działa również ze stałą liczbą wierszy (zamiast 0 / nieograniczoną)
bgolson
Nie działało to dla mnie z jawnie preferowanym ustawieniem maksymalnej szerokości, więc upewnij się, że jest ustawione na automatyczne w IB podczas korzystania z tej techniki. Pozdrawiam
jmc
Ustawienie automatycznej maksymalnej szerokości jest dostępne tylko w iOS8. Z mojego zrozumienia automatycznie dostosuje również wysokość i z mojego doświadczenia wynika, że ​​nie trzeba ustawiać ograniczenia wysokości, aby działało. To działało dla mnie przy użyciu wyraźnej szerokości.
Tony
1
To nie działało dla mnie. Uważam, że stackoverflow.com/a/13032251/1529675 jest poprawną odpowiedzią.
tboyce12,
48

Źródło: http://www.objc.io/issue-3/advanced-auto-layout-toolbox.html

Rozmiar zawartości wewnętrznej tekstu wielowierszowego

Rzeczywisty rozmiar zawartości UILabel i NSTextField jest niejednoznaczny w przypadku tekstu wielowierszowego. Wysokość tekstu zależy od szerokości linii, która nie została jeszcze ustalona przy rozwiązywaniu ograniczeń. W celu rozwiązania tego problemu obie klasy mają nową właściwość o nazwie preferowanyMaxLayoutWidth, która określa maksymalną szerokość linii do obliczania wewnętrznej wielkości zawartości.

Ponieważ zwykle nie znamy tej wartości z wyprzedzeniem, musimy zastosować dwustopniowe podejście, aby uzyskać tę prawidłowość. Najpierw pozwalamy Auto Layout wykonać swoją pracę, a następnie wykorzystujemy wynikową ramkę w przebiegu układu, aby zaktualizować preferowaną maksymalną szerokość i ponownie uruchomić układ.

- (void)layoutSubviews
{
    [super layoutSubviews];
    myLabel.preferredMaxLayoutWidth = myLabel.frame.size.width;
    [super layoutSubviews];
}

Pierwsze wywołanie [super layoutSubviews] jest konieczne, aby etykieta mogła ustawić ramkę, a drugie wywołanie jest konieczne do zaktualizowania układu po zmianie. Jeśli pominiemy drugie wywołanie, otrzymamy błąd NSInternalInconsistencyException, ponieważ dokonaliśmy zmian w przebiegu układu, które wymagają aktualizacji ograniczeń, ale nie uruchomiliśmy układu ponownie.

Możemy to również zrobić w samej podklasie etykiet:

@implementation MyLabel
- (void)layoutSubviews
{
    self.preferredMaxLayoutWidth = self.frame.size.width;
    [super layoutSubviews];
}
@end

W tym przypadku nie musimy najpierw wywoływać [super layoutSubviews], ponieważ kiedy wywoływany jest layoutSubviews, mamy już ramkę na samej etykiecie.

Aby dokonać tej regulacji z poziomu kontrolera widoku, przechodzimy do viewDidLayoutSubviews. W tym momencie ramki pierwszego przebiegu automatycznego układu są już ustawione i możemy ich użyć, aby ustawić preferowaną maksymalną szerokość.

- (void)viewDidLayoutSubviews
{
    [super viewDidLayoutSubviews];
    myLabel.preferredMaxLayoutWidth = myLabel.frame.size.width;
    [self.view layoutIfNeeded];
}

Na koniec upewnij się, że nie masz wyraźnego ograniczenia wysokości na etykiecie, która ma wyższy priorytet niż priorytet odporności na kompresję zawartości etykiety. W przeciwnym razie przebije obliczoną wysokość zawartości. Sprawdź wszystkie ograniczenia, które mogą wpłynąć na wysokość etykiety.

Anton Matosov
źródło
dziękuję dziękuję dziękuję nie tylko za kod, ale co ważniejsze wyjaśnienie i przykłady, jak to zrobić nie tylko w podklasie, ale również z kontrolera widoku.
djibouti33
Próbuję tego w moim kontrolerze widoku, a viewDidLayoutSubviews jest wywoływany po viewWillAppear i viewDidAppear. Po viewDidAppear następuje błysk, gdy rozmiar etykiet jest zmieniany poprawnie. czy jest jakiś sposób, aby tego uniknąć? czy jest jakiś sposób na zakończenie zmiany rozmiaru, zanim użytkownik coś zobaczy? w moim przypadku moje wieloliniowe etykiety znajdują się w tableHeaderView, więc zmieniam także wysokość widoku nagłówka na podstawie etykiet, korzystając z tego przykładu ( stackoverflow.com/questions/20982558/... )
djibouti33
@ djibouti33 czy możesz podać minimalny przykładowy kod (np. w formacie github repo), abym mógł łatwo sprawdzić Twój problem?
Anton Matosov
Dzięki @Anton. Od tego czasu nasz projekt zmienił się z UITableView na UICollectionView i na szczęście tego nie widziałem. Jeśli uda mi się przejrzeć historię gitów i stworzyć mały przykładowy projekt, dam ci znać.
djibouti33
Problem dla mnie, odkąd zacząłem działać na systemie iOS 9, ale kiedy testowałem na iOS 8, zaczęły się problemy. Muszę ręcznie ustawić maksymalną szerokość.
naz
17

Właśnie walczyłem z tym dokładnie scenariuszem, ale z kilkoma dodatkowymi widokami, które musiały zmienić rozmiar i przesuwać się w razie potrzeby. Doprowadzało mnie to do szału, ale w końcu to rozgryzłem.

Oto klucz: Konstruktor interfejsów lubi wprowadzać dodatkowe ograniczenia podczas dodawania i przenoszenia widoków i możesz tego nie zauważyć. W moim przypadku miałem widok w połowie drogi, który miał dodatkowe ograniczenie, które określało rozmiar między nim a jego nadzorem, po prostu przypinając go do tego punktu. Oznaczało to, że nic powyżej tego nie mogło zmienić rozmiaru, ponieważ byłoby sprzeczne z tym ograniczeniem.

Prostym sposobem na stwierdzenie, czy tak jest, jest próba ręcznej zmiany rozmiaru etykiety. Czy IB pozwala ci to wyhodować? Jeśli tak, czy poniższe etykiety poruszają się zgodnie z oczekiwaniami? Upewnij się, że oba te elementy są zaznaczone przed zmianą rozmiaru, aby zobaczyć, w jaki sposób ograniczenia spowodują przesunięcie widoków:

Menu IB

Jeśli widok jest zablokowany, postępuj zgodnie z widokami pod nim i upewnij się, że jeden z nich nie ma górnej przestrzeni do ograniczenia podglądu. Następnie upewnij się, że opcja liczby linii dla etykiety jest ustawiona na 0, a resztę zajmie resztę.

Cory Imdieke
źródło
Tak, po wielu zabawach udało mi się uzyskać pożądany efekt. Musisz bardzo dokładnie przemyśleć ograniczenia, które chcesz. Nie pomaga to, że IB ciągle wprowadza ograniczenia, których się nie spodziewasz.
James Harpe,
5

Uważam, że potrzebujesz następujących elementów:

  • Górne ograniczenie
  • Wiodące ograniczenie (np. Lewa strona)
  • Ograniczenie końcowe (np. Prawa strona)
  • Ustaw priorytet przytulania treści, od poziomego do niskiego, aby wypełnił podane miejsce, jeśli tekst jest krótki.
  • Ustaw odporność na kompresję treści, poziomą na niską, aby zawijała się zamiast próbować się poszerzać.
  • Ustaw liczbę linii na 0.
  • Ustaw tryb podziału linii na zawijanie wyrazów.
Chris
źródło
Powinno to działać w xcode9. Nie musiałem nic robić z ograniczeniem wysokości etykiety (nie mam żadnego): to po prostu działa po wyjęciu z pudełka, gdy etykieta zostanie ograniczona (trailing, wiodąca, góra i dół). uikit zajmie się resztą
Anton Tropashko,
Priorytetem przytulałem treść. Dziękujemy za poinformowanie mnie o tej właściwości, a także o odporności na ściskanie. Pomogłeś mi rozwiązać godzinny problem.
David Gay
0

Żadne z różnych rozwiązań znalezionych w wielu tematach na ten temat nie działało idealnie w moim przypadku (x dynamiczne etykiety wielowierszowe w komórkach dynamicznego widoku tabeli).

Znalazłem sposób, aby to zrobić:

Po ustawieniu ograniczeń na etykiecie i ustawieniu jej właściwości multilinii na 0, utwórz podklasę UILabel; Zadzwoniłem do mojego AutoLayoutLabel:

@implementation AutoLayoutLabel

- (void)layoutSubviews{
    [self setNeedsUpdateConstraints];
    [super layoutSubviews];
    self.preferredMaxLayoutWidth = CGRectGetWidth(self.bounds);
}

@end
Tanguy G.
źródło
0

Mam UITableViewCell, który ma etykietę zawijania tekstu. Pracowałem nad zawijaniem tekstu w następujący sposób.

1) Ustaw ograniczenia UILabel w następujący sposób.

wprowadź opis zdjęcia tutaj

2) Zestaw nr linii do 0.

3) Dodano ograniczenie wysokości UILabel do UITableViewCell.

@IBOutlet weak var priorityLabelWidth: NSLayoutConstraint!

4) W UITableViewCell:

priorityLabel.sizeToFit()
priorityLabelWidth.constant = priorityLabel.intrinsicContentSize().width+5
AG
źródło
-12

Jeden ze sposobów, aby to zrobić ... Gdy długość tekstu rośnie, spróbuj zmienić (zmniejszyć) rozmiar czcionki tekstu etykiety za pomocą

Label.adjustsFontSizeToFitWidth = YES;
użytkownik1662392
źródło
1
Chcę, aby różne wystąpienia kontrolera widoku miały spójny wygląd, więc nie chcę zmieniać rozmiaru czcionki.
James Harpe