2014-04-30 20 views
29

Tôi đang cố gắng tìm hiểu bố cục tự động, cuối cùng nghĩ rằng tôi đã nhận được treo trên nó khi điều này xảy ra. Tôi đang chơi về với các tế bào nguyên mẫu và nhãn. Tôi đang cố gắng để có một Tiêu đềAutoLayout multiline UILabel cắt bỏ một số văn bản

  • , titleLbl - hộp màu xanh trong ảnh
  • Money, moneyLbl - hộp màu xanh lá cây trong ảnh
  • Subtitle/mô tả, subTitleLbl - hộp màu đỏ trong hình ảnh

Cho đến nay tôi có điều này:

iphone screenshot

.210

Những gì tôi muốn đạt được là:

  • Hộp màu xanh lá cây nên luôn luôn hiển thị trên 1 dòng đầy đủ
  • Giá trị bên trong hộp màu xanh lá cây nên được tập trung dựa trên các hộp màu xanh
  • Hộp xanh sẽ mất khoảng trống còn lại (-8px cho giới hạn ngang giữa 2) và hiển thị trên nhiều dòng nếu cần
  • Hộp màu đỏ phải ở dưới và hiển thị nhiều dòng nếu cần.

Như bạn có thể thấy điều này gần như tất cả đang hoạt động, ngoại trừ ví dụ ở hàng cuối cùng. Tôi đã cố gắng nghiên cứu này và từ những gì tôi có thể thu thập nó là một cái gì đó để làm với kích thước nội dung nội tại. Nhiều bài đăng trực tuyến khuyên bạn nên đặt setPreferredMaxLayoutWidth, tôi đã thực hiện việc này ở nhiều nơi cho số titleLbl và nó không có hiệu lực. Chỉ khi hardcoding nó để 180 tôi đã nhận được kết quả, nhưng nó cũng thêm khoảng trắng/padding cho các hộp màu xanh khác trên đầu/bên dưới văn bản, làm tăng đáng kể không gian giữa các hộp màu đỏ/xanh.

Dưới đây là những hạn chế của tôi:

Title: title constraints

Money: money constraints

Subtitle: subtitle constraints

Dựa trên các bài kiểm tra tôi đã chạy với mẫu văn bản khác nó dường như đang sử dụng chiều rộng như được hiển thị trong bảng phân cảnh, nhưng bất kỳ nỗ lực sửa lỗi nào đều không hoạt động.

Tôi đã tạo ra một Ivar của tế bào và sử dụng nó để tạo ra chiều cao của tế bào:

- (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; 
} 

Tôi đã thử đặt setPreferredMaxLayoutWidth bên subviews bố trí của tế bào:

- (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]; 
} 

log :

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 

Tôi mong đợi điều gì khi có 8px giữa 2 yếu tố và giao diện điều khiển xác minh điều này.Nó hoạt động hoàn hảo trên hàng đầu tiên cho dù có bao nhiêu văn bản tôi thêm vào hộp màu xanh, giống như bảng phân cảnh, nhưng bất kỳ sự gia tăng nào vào hộp màu xanh lá cây đều làm giảm tính toán chiều rộng. Tôi đã thử đặt cài đặt này trong số tableView:willDisplayCelltableView:heightForRowAtIndexPath: cũng dựa trên câu trả lời từ các câu hỏi khác. Nhưng không có vấn đề gì nó chỉ không bố trí.

Bất kỳ sự giúp đỡ, ý tưởng hay ý kiến ​​sẽ được đánh giá rất

Trả lời

26

Tìm thấy một câu trả lời tốt hơn sau khi đọc bài viết này: http://johnszumski.com/blog/auto-layout-for-table-view-cells-with-dynamic-heights

tạo một lớp con UILabel để ghi đè layoutSubviews như vậy:

- (void)layoutSubviews 
{ 
    [super layoutSubviews]; 

    self.preferredMaxLayoutWidth = CGRectGetWidth(self.bounds); 

    [super layoutSubviews]; 
} 

Điều này đảm bảo preferredMaxLayoutWidth luôn là đúng.

Cập nhật:

Sau nhiều phiên bản hơn của iOS và thử nghiệm của hơn usecase tôi đã tìm thấy ở trên để không đủ cho mọi trường hợp. Tính đến iOS 8 (tôi tin), trong một số trường hợp chỉ có didSetbounds được gọi đúng lúc trước khi tải màn hình.

Trong iOS 9 rất gần đây tôi tình cờ gặp một vấn đề khác khi sử dụng một phương thức UIVIewController với một UITableView, rằng cả hai layoutSubviewsset bounds đã được gọi là sauheightForRowAtIndexPath. Sau nhiều lần gỡ lỗi, giải pháp duy nhất là ghi đè set frame.

Mã dưới đây bây giờ có vẻ là cần thiết để đảm bảo nó hoạt động trên tất cả các của iOS, điều khiển, kích cỡ màn hình, vv

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

Tôi không chắc là tôi hiểu bạn ngay và giải pháp của tôi là thực sự xa tốt nhất, nhưng ở đây nó là:

Tôi đã thêm một hạn chế chiều cao trên nhãn đã được cắt, kết nối ràng buộc đó với đầu ra của ô. Tôi đã ghi đè phương thức layoutSubview:

- (void) layoutSubviews { 
    [super layoutSubviews]; 

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

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

và viola! Hãy thử phương pháp đó và cho tôi biết kết quả, cảm ơn bạn.

+0

cảm ơn đầu vào, nhưng tôi xin lỗi vì không có cách nào khác. Vấn đề (như được nêu trong câu trả lời của tôi) là nó đang sử dụng vị trí 'X' từ nhãn màu xanh lá cây trước khi nhãn màu xanh lá cây được kéo dài ra để vừa với nội dung của nó. Khi sử dụng các nhãn nhiều dòng trong hầu hết các trường hợp, bạn cần đặt 'setPreferredMaxLayoutWidth', tôi cần tính toán độ rộng được hiển thị của màu xanh lục để tính giá trị này cho màu xanh lam. Một khi đã được thiết lập, nó đã tìm ra chiều cao chính xác –

0

Omg, chỉ có cùng một vấn đề. Câu trả lời của @Simon McLoughlin không giúp tôi. Nhưng

titleLabel.adjustsFontSizeToFitWidth = true 

đã làm một phép thuật! Nó hoạt động, tôi không biết tại sao nhưng nó. Và có, nhãn của bạn phải có giá trị ưa thích rõ ràngMaxLayoutWidth.

+2

Đây không thực sự là một giải pháp lý tưởng. Thay vì cho phép nhãn phát triển, điều này đơn giản là làm cho phông chữ tăng/giảm để phù hợp với chiều rộng cố định. Điều này sẽ dẫn đến giao diện người dùng của bạn trông rất không nhất quán. Tôi đã tìm thấy nhiều lỗi hơn trong iOS và sử dụng các trường hợp yêu cầu mã bổ sung. Tôi đã cập nhật câu trả lời của mình ở trên. –

0

tôi đã dành chút thời gian với gói lên các tế bào, nhìn như thế này:

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

Sau nhiều nỗ lực tôi đã đi kèm với một giải pháp làm thế nào để sử dụng tế bào nguyên mẫu được thực sự làm việc.

Vấn đề chính là nhãn của tôi có thể hoặc không thể ở đó. Mọi phần tử phải là tùy chọn.

Trước hết, ô 'nguyên mẫu' của chúng tôi chỉ nên bố cục nội dung khi chúng ta không ở trong môi trường "thực".

Vì vậy, tôi đã tạo cờ 'heightCalculationInProgress'.

Khi ai đó (dataView nguồn của tableView) yêu cầu tính chiều cao, chúng tôi đang cung cấp dữ liệu cho ô mẫu thử sử dụng khối: Và sau đó chúng tôi yêu cầu ô mẫu thử bố cục nội dung và lấy chiều cao. Đơn giản.

Để tránh iOS7 lỗi với layoutSubview đệ quy có một "layoutingLock" biến

- (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; 
} 

'khối' này từ bên ngoài có thể trông như thế này:

[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 
}]; 

Và bây giờ bắt đầu phần thú vị nhất. Bố cục:

- (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; 
    } 
} 
Các vấn đề liên quan