2014-04-30 11 views
29

Próbuję nauczyć się automatycznego układu, w końcu pomyślałem, że rozumiem, kiedy to się stało. Gram o prototypowych komórkach i etykietach. Staram się miećAutoLayout multilinalne UILabel odcinające trochę tekstu

  • Tytuł, titleLbl - niebieskie pole w obrazie
  • Pieniądze, moneyLbl - zielone pole w obrazie
  • napisów/opis, subTitleLbl - czerwone pole w obrazie

Do tej pory mam to:

iphone screenshot

Co chcę osiągnąć jest:

  • Zielona skrzynka powinna zawsze wyświetlane na 1 linię w pełni
  • Wartość środku zielonego pola powinny być skoncentrowane na podstawie niebieskim polu
  • Niebieska skrzynka odbędzie pozostała przestrzeń (-8px dla ograniczenia poziomego między 2) i wyświetlanie na wielu liniach, jeśli to konieczne
  • Czerwone pole powinno znajdować się pod spodem i wyświetlać tyle wierszy, ile potrzeba.

Jak widać, prawie wszystko działa, z wyjątkiem przykładu z ostatniego rzędu. Próbowałem to zbadać i z tego, co mogę zebrać, wynika, że ​​ma to związek z wewnętrzną wielkością treści. Wiele postów poleca online ustawienie setPreferredMaxLayoutWidth, zrobiłem to w wielu miejscach dla titleLbl i nie miało to żadnego efektu. Dopiero po zakodowaniu go na 180 otrzymałem wyniki, ale dodałem również białe znaki do innych niebieskich pól na górze/pod tekstem, znacznie zwiększając przestrzeń między czerwonymi i niebieskimi polami.

Oto moje ograniczenia:

Tytuł: title constraints

Pieniądze: money constraints

Napisy: subtitle constraints

podstawie testów Zabrakło mi z innych próbek tekstowych Czyni wydaje się, że używa szerokości, jak pokazano w scenorysie, ale żadna próba jej poprawienia nie działa.

I stworzyli ivar komórki i wykorzystał je do stworzenia wysokości komórki:

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath 
{ 
    [sizingCellTitleSubMoney.titleLbl setText:[[tableViewData objectAtIndex:indexPath.row] objectForKey:@"title"]]; 
    [sizingCellTitleSubMoney.subtitleLbl setText:[[tableViewData objectAtIndex:indexPath.row] objectForKey:@"subtitle"]]; 
    [sizingCellTitleSubMoney.moneyLbl setText:[[tableViewData objectAtIndex:indexPath.row] objectForKey:@"cost"]]; 

    [sizingCellTitleSubMoney layoutIfNeeded]; 

    CGSize size = [sizingCellTitleSubMoney.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize]; 
    return size.height+1; 
} 

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 
{ 
    MATitleSubtitleMoneyTableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:CellIdentifierTitleSubTitleMoney]; 

    if(!cell) 
    { 
     cell = [[MATitleSubtitleMoneyTableViewCell alloc] init]; 
    } 


    cell.titleLbl.layer.borderColor = [UIColor blueColor].CGColor; 
    cell.titleLbl.layer.borderWidth = 1; 

    cell.subtitleLbl.layer.borderColor = [UIColor redColor].CGColor; 
    cell.subtitleLbl.layer.borderWidth = 1; 

    cell.moneyLbl.layer.borderColor = [UIColor greenColor].CGColor; 
    cell.moneyLbl.layer.borderWidth = 1; 


    [cell.titleLbl setText:[[tableViewData objectAtIndex:indexPath.row] objectForKey:@"title"]]; 
    [cell.subtitleLbl setText:[[tableViewData objectAtIndex:indexPath.row] objectForKey:@"subtitle"]]; 
    [cell.moneyLbl setText:[[tableViewData objectAtIndex:indexPath.row] objectForKey:@"cost"]]; 

    return cell; 
} 

próbowałem zachodzącego setPreferredMaxLayoutWidth wewnątrz komórki za subviews układ:

- (void)layoutSubviews 
{ 
    [super layoutSubviews]; 

    NSLog(@"Money lbl: %@", NSStringFromCGRect(self.moneyLbl.frame)); 
    NSLog(@"prefferredMaxSize: %f \n\n", self.moneyLbl.frame.origin.x-8); 

    [self.titleLbl setPreferredMaxLayoutWidth: self.moneyLbl.frame.origin.x-8]; 
} 

dziennika :

2014-04-30 21:37:32.475 AutoLayoutTestBed[652:60b] Money lbl: {{209, 20}, {91, 61}} 
2014-04-30 21:37:32.475 AutoLayoutTestBed[652:60b] prefferredMaxSize: 201.000000 

Czego się bym nie spodziewał, ponieważ między 2 elementami występuje 8px i konsola to weryfikuje.Działa to doskonale w pierwszym rzędzie, bez względu na to, ile tekstu dodaję do niebieskiego pudełka, które jest takie samo, jak scenorys, ale jakikolwiek wzrost do zielonego pudełka powoduje odrzucenie obliczenia szerokości. Próbowałem ustawić to w tableView:willDisplayCell i tableView:heightForRowAtIndexPath:, jak również na podstawie odpowiedzi z innych pytań. Ale bez względu na to, co po prostu nie układa.

Każda pomoc, pomysły i uwagi będą bardzo mile widziane

Odpowiedz

26

Znaleziony jeszcze lepszą odpowiedź po przeczytaniu tego: http://johnszumski.com/blog/auto-layout-for-table-view-cells-with-dynamic-heights

tworząc UILabel podklasy zastąpić layoutSubviews tak:

- (void)layoutSubviews 
{ 
    [super layoutSubviews]; 

    self.preferredMaxLayoutWidth = CGRectGetWidth(self.bounds); 

    [super layoutSubviews]; 
} 

Zapewnia to preferredMaxLayoutWidth jest zawsze poprawne.

Aktualizacja:

Po kilku bardziej release na iOS i testowania więcej USECASE tych znalazłem wyżej nie być wystarczająca dla każdego przypadku. Począwszy od systemu iOS 8 (jak sądzę) w niektórych okolicznościach wywołano tylko didSetbounds przed załadowaniem ekranu.

W iOS 9 niedawno natknąłem się na innej kwestii przy użyciu modalnego UIVIewController z UITableView, że zarówno layoutSubviews i set bounds były nazywane poheightForRowAtIndexPath. Po długim debugowaniu jedynym rozwiązaniem było zastąpienie set frame.

Poniższy kod wydaje się być konieczne, aby upewnić się, że działa na wszystkich iOS, sterowniki, rozmiarów ekranu itp

override public func layoutSubviews() 
{ 
    super.layoutSubviews() 
    self.preferredMaxLayoutWidth = self.bounds.width 
    super.layoutSubviews() 
} 

override public var bounds: CGRect 
{ 
    didSet 
    { 
     self.preferredMaxLayoutWidth = self.bounds.width 
    } 
} 

override public var frame: CGRect 
{ 
    didSet 
    { 
     self.preferredMaxLayoutWidth = self.frame.width 
    } 
} 
2

Nie jestem pewien, czy rozumiem cię i moje rozwiązanie jest naprawdę daleko od najlepszy, ale tutaj jest:

mam dodano ograniczenie wysokości na wycinanej etykiecie, co wiązało się z ograniczeniem wylotu komórki. Zastąpiłem metodę layoutSubview:

- (void) layoutSubviews { 
    [super layoutSubviews]; 

    [self.badLabel setNeedsLayout]; 
    [self.badLabel layoutIfNeeded]; 

    self.badLabelHeightConstraint.constant = self.badLabel.intrinsicContentSize.height; 
} 

i viola! Spróbuj tej metody i powiedz mi wyniki, dziękuję.

+0

dzięki za wejście, ale przykro mi, nie, to jest daleko. Problem (zgodnie z moją odpowiedzią) polega na tym, że używałem pozycji "X" z zielonej etykiety, zanim zielona etykieta została rozciągnięta, aby pasowała do jej treści. W przypadku używania etykiet wielowierszowych w większości przypadków należy ustawić opcję 'setPreferredMaxLayoutWidth', aby obliczyć tę wartość dla koloru niebieskiego, muszę obliczyć wyświetlaną szerokość zieleni. Po ustawieniu ustalono prawidłową wysokość. –

0

Omg, po prostu mieli ten sam problem. Odpowiedź @Simona McLoughlina nie pomogła mi. Ale zrobiła magię! Działa, nie wiem dlaczego, ale tak. I tak, twoja etykieta musi mieć wyraźną wartość preferredMaxLayoutWidth.

+2

To naprawdę nie jest idealne rozwiązanie. Zamiast pozwalać na wzrost etykiety, po prostu zwiększy się/zmniejszy czcionkę do określonej szerokości. Spowoduje to, że Twój interfejs użytkownika będzie bardzo niespójny. Znalazłem więcej błędów w iOS i wykorzystałem przypadki, które wymagały dodatkowego kodu. Zaktualizowałem swoją odpowiedź powyżej. –

0

zrobiłem spędzić trochę czasu z podsumowującej komórkę, patrząc tak:

+--------------------------------------------------+ 
| This is my cell.contentView      | 
| +------+ +-----------------+ +--------+ +------+ | 
| |  | | multiline | | title2 | |  | | 
| | img | +-----------------+ +--------+ | img | | 
| |  | +-----------------+ +--------+ |  | | 
| |  | |  title3  | | title4 | |  | | 
| +------+ +-----------------+ +--------+ +------+ | 
|             | 
+--------------------------------------------------+ 

Po wielu próbach zrobiłem pochodziły z roztworem, jak wykorzystać komórkę prototyp, który jest faktycznie działa.

Głównym problemem było to, że moje etykiety mogły być lub mogą tam nie być. Każdy element powinien być opcjonalny.

Po pierwsze, nasza komórka "prototypowa" powinna tylko układać elementy, gdy nie jesteśmy w "prawdziwym" środowisku.

Dlatego utworzono flagę "heightCalculationInProgress".

Kiedy ktoś (źródło danych tableView's) prosi o obliczenie wysokości, dostarczamy dane do prototypowej komórki za pomocą bloku: Następnie prosimy naszą komórkę prototypową o zaprojektowanie zawartości i wyniesienie z niej wysokości. Prosty.

W celu uniknięcia iOS7 błąd z layoutSubview rekursji jest „layoutingLock” zmienna

- (CGFloat)heightWithSetupBlock:(nonnull void (^)(__kindof UITableViewCell *_Nonnull cell))block { 
    block(self); 

    self.heightCalculationInProgress = YES; 

    [self setNeedsLayout]; 
    [self layoutIfNeeded]; 

    self.layoutingLock = NO; 
    self.heightCalculationInProgress = NO; 

    CGFloat calculatedHeight = [self.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height; 
    CGFloat height = roundf(MAX(calculatedHeight, [[self class] defaultHeight])); 

    return height; 
} 

Ten „blok” z zewnątrz może wyglądać następująco:

[prototypeCell heightWithSetupBlock:^(__kindof UITableViewCell * _Nonnull cell) { 
    @strongify(self); 
    cell.frame = CGRectMake(0, 0, CGRectGetWidth(self.view.frame), 0); 
    cell.dataObject = dataObject; 
    cell.multilineLabel.text = @"Long text"; 
    // Include any data here 
}]; 

A teraz rozpoczyna się najciekawsza część. Układ:

- (void)layoutSubviews { 
    if(self.layoutingLock){ 
     return; 
    } 

    // We don't want to do this layouting things in 'real' cells 
    if (self.heightCalculationInProgress) { 

    // Because of: 
    // Support for constraint-based layout (auto layout) 
    // If nonzero, this is used when determining -intrinsicContentSize for multiline labels 

    // We're actually resetting internal constraints here. And then we could reuse this cell with new data to layout it correctly 
     _multilineLabel.preferredMaxLayoutWidth = 0.f; 
     _title2Label.preferredMaxLayoutWidth = 0.f; 
     _title3Label.preferredMaxLayoutWidth = 0.f; 
     _title4Label.preferredMaxLayoutWidth = 0.f; 
    } 

    [super layoutSubviews]; 

    // We don't want to do this layouting things in 'real' cells 
    if (self.heightCalculationInProgress) { 

     // Make sure the contentView does a layout pass here so that its subviews have their frames set, which we 
     // need to use to set the preferredMaxLayoutWidth below. 
     [self.contentView setNeedsLayout]; 
     [self.contentView layoutIfNeeded]; 

     // Set the preferredMaxLayoutWidth of the mutli-line bodyLabel based on the evaluated width of the label's frame, 
     // as this will allow the text to wrap correctly, and as a result allow the label to take on the correct height. 
     _multilineLabel.preferredMaxLayoutWidth = CGRectGetWidth(_multilineLabel.frame); 
     _title2Label.preferredMaxLayoutWidth = CGRectGetWidth(_title2Label.frame); 
     _title3Label.preferredMaxLayoutWidth = CGRectGetWidth(title3Label.frame); 
     _title4Label.preferredMaxLayoutWidth = CGRectGetWidth(_title4Label.frame); 
    } 

    if (self.heightCalculationInProgress) { 
     self.layoutingLock = YES; 
    } 
} 
Powiązane problemy