Wiele etykiet UILabel wewnątrz SELF sizing UITableViewCell

W tej aplikacji na iOS 8, którą tworzę, mam widok tableview i potrzebuję ich do samodzielnej zmiany rozmiaru. Zaimplementowałem go za pomocą Auto layoutu i działa. Prawie. Oto jak to teraz wygląda.

Tutaj wpisz opis obrazka

Wewnątrz komórki znajdują się 3 etykiety. Główna etykieta, która ma tekst Lorem ipsum. Podtytuł, który ma ciąg cyfr (są to dwie oddzielne etykiety. Może być mylące, ponieważ mają ten sam kolor.) Następnie trzecia etykieta z małym czarnym tekstem.

Pierwsza etykieta została zmieniona sama poprawnie bez problemu, a druga Etykieta przesuwa się odpowiednio w górę iw dół. Ale problem jest z trzecią małą etykietą. Jak widać, nie zmienia rozmiaru, aby pasował do całego tekstu.

Teraz dzieje się coś dziwnego. Obracam go i oto jest.

Tutaj wpisz opis obrazka

Ponieważ jest spacja, Etykieta wyświetla cały tekst, do którego ma być przeznaczona. Dobrze. Potem wracam do portretu.

Tutaj wpisz opis obrazka

Teraz mała Etykieta zmieniła rozmiar na pasuje do całego tekstu, ale przepełnia granice komórek. Próbowałem powiększyć celę, ale nie zadziałało. Ponieważ jest to self sizing komórek, nie sądzę, że jest to poprawny sposób nawet.

Nie dostaję żadnych błędów, ani nawet ostrzeżenia o ograniczeniach układu auto.

Tutaj wpisz opis obrazka

Ustawiłem te dwie linie kodu w metodzie viewDidLoad().

tableView.estimatedRowHeight = 100
tableView.rowHeight = UITableViewAutomaticDimension
Czy ktoś może mi powiedzieć, co robię źle?

Ponieważ trudno jest odpowiedzieć tylko patrząc na Zdjęcia i nie mam więcej kodu do zamieszczenia obok powyższego fragmentu, wgrałem uruchomiony projekt Xcode demonstrujący problem tutaj . (Istnieją 2 niestandardowe komórki. Zasadniczo to samo ogniwo tylko wysokość jest zwiększona w drugim.)

Majstrowałem z ograniczeniami auto layoutu, ale nie mogę tego uruchomić. Każda pomoc będzie mile widziana.

Dziękuję.

UPDATE:

Z pomocą tego tutoriala I znalazłem pomocne wskazówki. Zgodnie z nim, każdy subview powinien mieć ograniczenia, które przypinają wszystkie jego boki i powinny istnieć ograniczenia, które przechodzą od góry do dołu, co pomaga układowi automatycznemu obliczyć wysokość komórki. W moim oryginalnym poście miałem pionowe spacje między każdą etykietą, więc myślę, że to dlatego układ auto nie mógł obliczyć odpowiedniej wysokości.

Więc wprowadziłem kilka zmian.
  • zmniejszyłem pionową przestrzeń między etykietami do 0 i ustawiłem pionową ograniczenia przestrzeni między etykietą górną i Środkową oraz etykietą Środkową i dolną.
  • dodałem ograniczenia prowadzące, górne, końcowe do górnej etykiety.
  • / Align = "left" /
  • początek, dół, ciągnięcie do dolnej etykiety.
Teraz kolejna dziwna część. Kiedy po raz pierwszy go uruchamiam, nadal istnieje problem z przycinaniem dolnej etykiety.

Tutaj wpisz opis obrazka

Ale jeśli obrócę urządzenie do poziomego i odwrócę je z powrotem do pionowego, wszystkie komórki są odpowiednio zmieniane, aby pasowały do obu etykiet!

Tutaj wpisz opis obrazka

Nadal nie mogę zrozumieć, dlaczego to się nie dzieje na początku. Zaktualizowany projekt Xcode jest tutaj .

Author: Isuru, 2014-09-20

5 answers

Problem dotyczy właściwości multi-line labels ' preferredMaxLayoutWidth. Jest to właściwość, która informuje Etykietę, kiedy powinna zawijać słowo. Musi być ustawiony poprawnie, aby każda etykieta intrinsicContentSize miała prawidłową wysokość, czyli ostatecznie jaki układ automatyczny będzie używany do określenia wysokości komórki.

Xcode 6 Interface Builder wprowadził nową opcję, aby mieć tę właściwość ustawioną na Automatic . Niestety, są pewne poważne błędy (od Xcode 6.2/iOS 8.2), gdzie nie jest to ustawione poprawnie / automatycznie podczas ładowania komórki ze stalówki lub storyboardu.

Aby obejść ten błąd, musimy mieć ustawioną preferredMaxLayoutWidth dokładnie równą ostatecznej szerokości etykiety po wyświetleniu jej w widoku tabeli. Przed zwróceniem komórki z tableView:cellForRowAtIndexPath: chcemy wykonać następujące czynności:

cell.nameLabel.preferredMaxLayoutWidth = CGRectGetWidth(cell.nameLabel.frame)
cell.idLabel.preferredMaxLayoutWidth = CGRectGetWidth(cell.idLabel.frame)
cell.actionsLabel.preferredMaxLayoutWidth = CGRectGetWidth(cell.actionsLabel.frame)

Powodem, dla którego samo dodanie tego kodu nie działa, jest to, że gdy te 3 linie kodu wykonają się w tableView:cellForRowAtIndexPath:, używamy szerokości każdego etykieta do Ustawienia preferredMaxLayoutWidth -- jednakże, jeśli w tym momencie sprawdzasz szerokość etykiet, szerokość etykiety jest zupełnie inna niż szerokość etykiety po wyświetleniu komórki i ułożeniu jej podglądów.

Jak uzyskać dokładne szerokości etykiet w tym momencie, aby odzwierciedlały ich ostateczną szerokość? Oto kod, który sprawia, że wszystko się składa:

// Inside of tableView:cellForRowAtIndexPath:, after dequeueing the cell

cell.bounds = CGRect(x: 0, y: 0, width: CGRectGetWidth(tableView.bounds), height: 99999)
cell.contentView.bounds = cell.bounds
cell.layoutIfNeeded()

cell.nameLabel.preferredMaxLayoutWidth = CGRectGetWidth(cell.nameLabel.frame)
cell.idLabel.preferredMaxLayoutWidth = CGRectGetWidth(cell.idLabel.frame)
cell.actionsLabel.preferredMaxLayoutWidth = CGRectGetWidth(cell.actionsLabel.frame)
Co my tu robimy? Zauważysz, że dodano 3 nowe linie kodu. Najpierw musimy ustawić szerokość komórki widoku tabeli tak, aby odpowiadała rzeczywistej szerokości widoku tabeli(zakłada się, że widok tabeli został już rozplanowany i ma swoją ostateczną szerokość, co powinno mieć miejsce). Skutecznie tylko wcześnie poprawiamy szerokość komórki, ponieważ widok tabeli w końcu to zrobi.

Zauważysz również, że używamy 99999 do wzrostu. O co chodzi? Jest to proste obejście problemu omówionego szczegółowo tutaj , gdzie jeśli Twoje ograniczenia wymagają więcej pionowej przestrzeni niż bieżąca wysokość contentView komórki, otrzymasz wyjątek ograniczenia, który nie wskazuje na żaden rzeczywisty problem. Wysokość komórki lub któregokolwiek z jej podglądów w rzeczywistości nie ma znaczenia w tym momencie, ponieważ zależy nam tylko na uzyskaniu końcowych szerokości dla każdej etykiety.

Następnie upewniamy się, że contentView komórki ma ten sam rozmiar, który właśnie przypisaliśmy do samej komórki, ustawiając granice contentView aby wyrównać granice komórki. Jest to konieczne, ponieważ wszystkie utworzone ograniczenia układu automatycznego są względne do widoku contentView, więc widok contentView musi mieć prawidłowy rozmiar, aby zostały poprawnie rozwiązane. Samo ręczne ustawienie rozmiaru komórki powoduje, że nie automatycznie dopasowuje widok zawartości.

Na koniec wymuszamy przekazywanie układu na komórce, która będzie miała silnik układu automatycznego rozwiązujący ograniczenia i aktualizujący ramki wszystkich podwidów. Od widok & contentView komórki ma teraz te same szerokości, które będą w czasie wykonywania w widoku tabeli, szerokości etykiet również będą poprawne, co oznacza, że preferredMaxLayoutWidth ustawione dla każdej etykiety będą dokładne i spowoduje zawinięcie etykiety we właściwym czasie, co oczywiście oznacza, że wysokość etykiet będzie ustawiona poprawnie, gdy komórka jest używana w widoku tabeli!

Jest to zdecydowanie błąd Apple w UIKit, który musimy obejść na razie (więc proszę zrobić plik raportów o błędach Z Apple, aby / align = "left" / ).

Ostatnia uwaga: To obejście spowoduje problemy, jeśli szerokość komórki widoku tabeli contentView nie rozszerzy pełnej szerokości widoku tabeli, na przykład gdy po prawej stronie znajduje się indeks sekcji. W takim przypadku musisz upewnić się, że ręcznie bierzesz to pod uwagę podczas ustawiania szerokości komórki - może być konieczne zakodowanie tych wartości na twardo, coś w stylu:

let cellWidth = CGRectGetWidth(tableView.bounds) - kTableViewSectionIndexWidth
cell.bounds = CGRect(x: 0, y: 0, width: cellWidth, height: 99999)
 79
Author: smileyborg,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-04-06 16:01:55

Spotkałem się z tym samym problemem co ty i znalazłem proste rozwiązanie, aby go rozwiązać.

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
     // dequeue cell...

     // do autolayout staffs...or if the autolayout rule has been set in xib, do nothing

     [cell layoutIfNeeded];

     return cell;
}

I samorozmiar działał dobrze. W moim kodzie położyłem dwie etykiety w pionie, obie są dynamiczną wysokością. Wysokość komórki jest prawidłowo ustawiona tak, aby zawierała dwie etykiety.

 48
Author: fogisland,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-11-08 02:46:29

Zakładając, że nie masz żadnych błędów w ograniczeniach, jak sugerowali inni, problem ten wydaje się wynikać z używania UILabel, który pozwala na wiele linii w połączeniu z UITableViewCellAccessory. Kiedy iOS rozkłada komórkę i określa wysokość, nie uwzględnia zmiany przesunięcia szerokości, która występuje z powodu tego akcesorium, a otrzymasz obcinanie tam, gdzie się nie spodziewasz.

Zakładając, że chcesz, aby etykieta UILabel rozszerzyła całą szerokość widoku zawartości, Napisałem metodę, która naprawia to dla wszystkich rozmiarów czcionek

-(void)fixWidth:(UILabel *)label forCell:(UITableViewCell *)cell {
    float offset = 0;
    switch ([cell accessoryType]) {
        case UITableViewCellAccessoryCheckmark:
            offset = 39.0;
            break;
        case UITableViewCellAccessoryDetailButton:
            offset = 47.0;
            break;
        case UITableViewCellAccessoryDetailDisclosureButton:
            offset = 67.0;
            break;
        case UITableViewCellAccessoryDisclosureIndicator:
            offset = 33.0;
            break;
        case UITableViewCellAccessoryNone:
            offset = 0;
            break;
    }
    [label setPreferredMaxLayoutWidth:CGRectGetWidth([[self tableView]frame]) - offset - 8];
}

Po prostu umieść to w swojej komórceforrowatindexpath

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    #Setup the cell
    ...
    // Fix layout with accessory view
    [self fixWidth:[cell label] forCell:cell];
    return cell;
}

Zrób to dla wszystkich etykiet, które będą miały wiele linii, aby odpowiednio dostosować szerokość, a następnie ponownie obliczyć odpowiednie wysokości. Działa to również z dynamicznymi rozmiarami czcionek.

Jak wspomniał smileyborg, jeśli nie używasz pełnej szerokości contentView, możesz odwołać się do ograniczeń i odjąć je od szerokości jako cóż.

Edit: wcześniej uruchamiałem 'layoutIfNeeded' na komórce, ale to powodowało problemy z wydajnością i nie wydawało się być potrzebne. Usunięcie go nie sprawiło mi żadnych problemów.

 6
Author: user2898617,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-02-28 16:46:04

Masz tu dwa problemy.

1 / teraz, używając języka formatu wizualnego, pionowe ograniczenia Twojej komórki można przetłumaczyć w następujący sposób:

Cell: "V:|-(10)-[nameLabel]-(67)-|"

Następnie ustawiasz drugą grupę ograniczeń:

Cell: "V:|-(10)-[nameLabel]-(8)-[pnrLabel]-(2)-[actionsLabel]"

Te dwie grupy ograniczeń nie mogą się dobrze wymieszać i ujawnią swoją dwuznaczność z drugim problemem.

2 / z pewnych powodów, actionsLabel jest ograniczona do jednej linii podczas uruchamiania aplikacji. Następnie, po obróceniu urządzenia do trybu poziomego, actionsLabel akceptuje wyświetlanie dwóch linii lub więcej. Następnie, po obróceniu urządzenia z powrotem do trybu portretowego, actionsLabel wyświetla dwie lub więcej linii. Ale ponieważ actionsLabel nie jest tak naprawdę częścią ograniczeń wysokości Twojej komórki, pokrywa się ona z granicami Twojej komórki.

Jeśli chcesz rozwiązać te wszystkie problemy, polecam najpierw odbudować plik xib od podstaw. To wyleczy Twoje actionsLabel dziwne zachowanie (dwie lub więcej linii tylko po obróceniu urządzenia).

Wtedy będziesz musisz zdefiniować swoje ograniczenia w ten sposób:

Cell: "V:|-(10)-[nameLabel(>=21)]-(8)-[pnrLabel(>=21)]-(2)-[actionsLabel(>=21)]-(10)-|"

Oczywiście możesz zdefiniować inne ograniczenia minimalnej wysokości dla etykiet niż (>=21). W ten sam sposób dolny margines można ustawić na inną wartość niż -(10)-.

Dodatek

Aby odpowiedzieć na twoje pytanie, stworzyłem prosty projekt z poprzednim wzorem ograniczeń w moim .plik xib. Poniższy obraz może pomóc ci zbudować własne ograniczenia.

Tutaj wpisz opis obrazka

 4
Author: Imanou Petit,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-09-20 19:02:37

[1]}wypróbowałem bardzo proste i elegancko wyglądające rozwiązanie "fogisland" - i nie zadziałało. Na szczęście dowiedziałem się, że jedna dodatkowa linia sprawia, że działa w każdym kierunku. Po prostu powiedz systemowi, że nie tylko sugerujesz nowy układ (layoutIfNeeded), ale wyraźnie o niego prosisz (setNeedLayout)

    cell.setNeedsLayout()
    cell.layoutIfNeeded()
    return cell
 1
Author: Hardy_Germany,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-03-16 20:05:34