Sử dụng Bố cục tự động trong UITableView để bố trí ô động & chiều cao hàng thay đổi


1501

Làm cách nào để bạn sử dụng Bố cục tự động trong UITableViewCells trong chế độ xem bảng để cho phép từng nội dung và nội dung của các ô xác định chiều cao hàng (chính nó / tự động), trong khi duy trì hiệu suất cuộn mượt mà?


Đây là mã mẫu trong swift 2.3 github.com/dpakthakur/DAVECellHeight
Deepak Thakur

Để hiểu hầu hết các câu trả lời liên quan đến việc có một UILabel đa dòng, bạn cần có sự hiểu biết đầy đủ về cách thức contentSizepreferredMaxLayoutWidthcông việc. Xem ở đây . Điều đó đang được nói nếu bạn thiết lập các ràng buộc của mình một cách chính xác thì bạn không cần preferredMaxLayoutWidthvà thực tế có thể tạo ra kết quả không mong muốn.
Mật ong

Câu trả lời:


2387

TL; DR: Không thích đọc sách? Chuyển thẳng đến các dự án mẫu trên GitHub:

Mô tả khái niệm

2 bước đầu tiên bên dưới được áp dụng cho dù bạn đang phát triển phiên bản iOS nào.

1. Thiết lập và thêm các ràng buộc

Trong UITableViewCelllớp con của bạn , hãy thêm các ràng buộc để các khung nhìn của ô có các cạnh được ghim vào các cạnh của contentView của ô (quan trọng nhất là các cạnh trên cùng VÀ dưới cùng). LƯU Ý: không ghim các cuộc phỏng vấn vào chính tế bào; Chỉ đến tế bào contentView! Hãy để kích thước nội dung nội tại của các lần xem trước này điều khiển chiều cao của chế độ xem nội dung của ô xem bảng bằng cách đảm bảo rằng các ràng buộc nén nội dung và các ràng buộc ôm nội dung theo chiều dọc cho mỗi lần xem phụ không bị ghi đè bởi các ràng buộc ưu tiên cao hơn mà bạn đã thêm. ( Hả? Bấm vào đây. )

Hãy nhớ rằng, ý tưởng là để các cuộc phỏng vấn của ô được kết nối theo chiều dọc với chế độ xem nội dung của ô để chúng có thể "gây áp lực" và làm cho chế độ xem nội dung mở rộng để phù hợp với chúng. Sử dụng một ô mẫu với một vài lần xem xét, đây là một minh họa trực quan về những gì một số (không phải tất cả!) Các ràng buộc của bạn sẽ cần trông giống như:

Ví dụ minh họa các ràng buộc trên một ô xem bảng.

Bạn có thể tưởng tượng rằng khi thêm nhiều văn bản vào nhãn cơ thể nhiều dòng trong ô ví dụ ở trên, nó sẽ cần phát triển theo chiều dọc để phù hợp với văn bản, điều này sẽ buộc tế bào tăng trưởng chiều cao một cách hiệu quả. (Tất nhiên, bạn cần phải có các ràng buộc ngay để điều này hoạt động chính xác!)

Làm cho các ràng buộc của bạn đúng chắc chắn là phần khó nhất và quan trọng nhất để có được độ cao của ô động làm việc với Bố cục tự động. Nếu bạn mắc lỗi ở đây, nó có thể ngăn mọi thứ khác hoạt động - vì vậy hãy dành thời gian của bạn! Tôi khuyên bạn nên thiết lập các ràng buộc của mình trong mã bởi vì bạn biết chính xác các ràng buộc nào đang được thêm vào ở đâu và việc gỡ lỗi sẽ dễ dàng hơn rất nhiều khi gặp sự cố. Việc thêm các ràng buộc trong mã có thể dễ dàng và mạnh hơn đáng kể so với Trình tạo giao diện bằng cách sử dụng các neo bố cục hoặc một trong các API nguồn mở tuyệt vời có sẵn trên GitHub.

  • Nếu bạn đang thêm các ràng buộc trong mã, bạn nên thực hiện việc này một lần từ trong updateConstraintsphương thức của lớp con UITableViewCell của bạn. Lưu ý rằng updateConstraintscó thể được gọi nhiều lần, do đó, để tránh thêm các ràng buộc giống nhau nhiều lần, hãy đảm bảo bọc mã bổ sung ràng buộc của bạn updateConstraintstrong một kiểm tra cho một thuộc tính boolean như didSetupConstraints(mà bạn đặt thành CÓ sau khi bạn chạy ràng buộc của mình -đánh dấu mã một lần). Mặt khác, nếu bạn có mã cập nhật các ràng buộc hiện có (chẳng hạn như điều chỉnh thuộc constanttính trên một số ràng buộc), hãy đặt mã này vào updateConstraintsnhưng bên ngoài kiểm tra didSetupConstraintsđể nó có thể chạy mỗi khi phương thức được gọi.

2. Xác định số nhận dạng tái sử dụng ô xem bảng duy nhất

Đối với mỗi bộ ràng buộc duy nhất trong ô, hãy sử dụng mã định danh tái sử dụng ô duy nhất. Nói cách khác, nếu các ô của bạn có nhiều hơn một bố cục duy nhất, mỗi bố cục duy nhất sẽ nhận được mã định danh tái sử dụng riêng. (Một gợi ý hay mà bạn cần sử dụng một mã định danh tái sử dụng mới là khi biến thể di động của bạn có số lần xem khác nhau hoặc các cuộc phỏng vấn được sắp xếp theo một kiểu riêng biệt.)

Ví dụ: nếu bạn đang hiển thị một thông báo email trong mỗi ô, bạn có thể có 4 bố cục duy nhất: tin nhắn chỉ có chủ đề, tin nhắn có chủ đề và nội dung, tin nhắn có chủ đề và tệp đính kèm ảnh và tin nhắn có chủ đề, cơ thể, và đính kèm hình ảnh. Mỗi bố cục có các ràng buộc hoàn toàn khác nhau cần thiết để đạt được nó, vì vậy một khi ô được khởi tạo và các ràng buộc được thêm vào một trong các loại ô này, ô sẽ nhận được một định danh tái sử dụng duy nhất cụ thể cho loại ô đó. Điều này có nghĩa là khi bạn hủy bỏ một ô để sử dụng lại, các ràng buộc đã được thêm vào và sẵn sàng cho loại ô đó.

Lưu ý rằng do sự khác biệt về kích thước nội dung bên trong, các ô có cùng ràng buộc (loại) vẫn có thể có độ cao khác nhau! Đừng nhầm lẫn giữa các bố cục khác nhau (các ràng buộc khác nhau) với các khung xem được tính toán khác nhau (được giải quyết từ các ràng buộc giống hệt nhau) do các kích thước nội dung khác nhau.

  • Không thêm các ô có các nhóm ràng buộc hoàn toàn khác nhau vào cùng một nhóm sử dụng lại (nghĩa là sử dụng cùng một mã định danh tái sử dụng) và sau đó cố gắng loại bỏ các ràng buộc cũ và thiết lập các ràng buộc mới từ đầu sau mỗi lần xử lý. Công cụ Bố cục tự động bên trong không được thiết kế để xử lý các thay đổi quy mô lớn trong các ràng buộc và bạn sẽ thấy các vấn đề hiệu suất lớn.

Dành cho iOS 8 - Các ô tự kích thước

3. Kích hoạt ước tính chiều cao hàng

Để bật các ô xem kích thước bảng tự kích thước, bạn phải đặt thuộc tính rowHeight của chế độ xem bảng thành UITableViewAutomaticDimension. Bạn cũng phải gán một giá trị cho thuộc tính ước tính. Ngay sau khi cả hai thuộc tính này được đặt, hệ thống sẽ sử dụng Bố cục tự động để tính chiều cao thực tế của hàng

Apple: Làm việc với các ô xem bảng tự kích thước

Với iOS 8, Apple đã nội bộ hóa nhiều công việc mà trước đây bạn phải triển khai trước iOS 8. Để cho phép cơ chế tế bào tự kích hoạt hoạt động, trước tiên bạn phải đặt thuộc rowHeighttính trên chế độ xem bảng thành hằng số UITableViewAutomaticDimension. Sau đó, bạn chỉ cần kích hoạt ước tính chiều cao hàng bằng cách đặt thuộc tính của chế độ xem bảng estimatedRowHeightthành giá trị khác không, ví dụ:

self.tableView.rowHeight = UITableViewAutomaticDimension;
self.tableView.estimatedRowHeight = 44.0; // set to whatever your "average" cell height is

Điều này không cung cấp cho chế độ xem bảng với ước tính tạm thời / giữ chỗ cho chiều cao hàng của các ô chưa được hiển thị trên màn hình. Sau đó, khi các ô này sắp cuộn trên màn hình, chiều cao hàng thực tế sẽ được tính. Để xác định chiều cao thực tế cho mỗi hàng, chế độ xem bảng sẽ tự động hỏi mỗi ô về chiều cao contentViewcần dựa trên chiều rộng cố định đã biết của chế độ xem nội dung (dựa trên chiều rộng của chế độ xem bảng, trừ đi mọi thứ bổ sung như chỉ mục phần hoặc chế độ xem phụ kiện) và các ràng buộc bố cục tự động mà bạn đã thêm vào chế độ xem nội dung và các nội dung của ô. Khi chiều cao ô thực tế này đã được xác định, chiều cao ước tính cũ cho hàng được cập nhật với chiều cao thực tế mới (và mọi điều chỉnh đối với contentSize / contentPackset của chế độ xem bảng được thực hiện khi cần thiết cho bạn).

Nói chung, ước tính bạn cung cấp không nhất thiết phải chính xác - nó chỉ được sử dụng để định cỡ chính xác chỉ báo cuộn trong chế độ xem bảng và chế độ xem bảng thực hiện tốt việc điều chỉnh chỉ báo cuộn cho các ước tính không chính xác như bạn di chuyển các ô trên màn hình. Bạn nên đặt thuộc estimatedRowHeighttính trên chế độ xem bảng (bằng viewDidLoadhoặc tương tự) thành giá trị không đổi là chiều cao hàng "trung bình". Chỉ khi chiều cao hàng của bạn có độ biến thiên cực lớn (ví dụ: khác nhau theo độ lớn) và bạn nhận thấy chỉ báo cuộn "nhảy" khi bạn cuộn, bạn nên thực hiện tableView:estimatedHeightForRowAtIndexPath:phép tính tối thiểu cần thiết để trả về ước tính chính xác hơn cho mỗi hàng.

Để được hỗ trợ iOS 7 (tự thực hiện kích thước ô tự động)

3. Thực hiện Giao diện và lấy chiều cao di động

Đầu tiên, khởi tạo một thể hiện ngoài màn hình của một ô xem bảng, một thể hiện cho mỗi mã định danh tái sử dụng , được sử dụng nghiêm ngặt để tính toán chiều cao. (Offscreen có nghĩa là tham chiếu ô được lưu trữ trong một thuộc tính / ivar trên bộ điều khiển chế độ xem và không bao giờ được quay lại từ tableView:cellForRowAtIndexPath:chế độ xem bảng để thực sự hiển thị trên màn hình.) Tiếp theo, ô phải được cấu hình với nội dung chính xác (ví dụ: văn bản, hình ảnh, v.v.) rằng nó sẽ giữ nếu nó được hiển thị trong chế độ xem bảng.

Sau đó, buộc các tế bào ngay lập tức bố trí subviews của nó, và sau đó sử dụng các systemLayoutSizeFittingSize:phương pháp trên UITableViewCell's contentViewđể tìm ra những chiều cao cần thiết của tế bào. Sử dụng UILayoutFittingCompressedSizeđể có được kích thước nhỏ nhất cần thiết để phù hợp với tất cả các nội dung của ô. Chiều cao sau đó có thể được trả về từ tableView:heightForRowAtIndexPath:phương thức ủy nhiệm.

4. Sử dụng ước tính Row Heights

Nếu chế độ xem bảng của bạn có nhiều hơn một vài hàng trong đó, bạn sẽ thấy rằng việc thực hiện giải quyết ràng buộc Bố cục tự động có thể nhanh chóng làm hỏng luồng chính khi tải lần đầu tiên vào chế độ xem bảng, như tableView:heightForRowAtIndexPath:được gọi trên mỗi và mỗi hàng khi tải lần đầu tiên ( để tính kích thước của chỉ báo cuộn).

Kể từ iOS 7, bạn có thể (và hoàn toàn nên) sử dụng thuộc estimatedRowHeighttính trên chế độ xem bảng. Điều này không cung cấp cho chế độ xem bảng với ước tính tạm thời / giữ chỗ cho chiều cao hàng của các ô chưa được hiển thị trên màn hình. Sau đó, khi các ô này sắp cuộn trên màn hình, chiều cao hàng thực tế sẽ được tính (bằng cách gọi tableView:heightForRowAtIndexPath:) và chiều cao ước tính được cập nhật với chiều cao thực tế.

Nói chung, ước tính bạn cung cấp không nhất thiết phải chính xác - nó chỉ được sử dụng để định cỡ chính xác chỉ báo cuộn trong chế độ xem bảng và chế độ xem bảng thực hiện tốt việc điều chỉnh chỉ báo cuộn cho các ước tính không chính xác như bạn di chuyển các ô trên màn hình. Bạn nên đặt thuộc estimatedRowHeighttính trên chế độ xem bảng (bằng viewDidLoadhoặc tương tự) thành giá trị không đổi là chiều cao hàng "trung bình". Chỉ khi chiều cao hàng của bạn có độ biến thiên cực lớn (ví dụ: khác nhau theo độ lớn) và bạn nhận thấy chỉ báo cuộn "nhảy" khi bạn cuộn, bạn nên thực hiện tableView:estimatedHeightForRowAtIndexPath:phép tính tối thiểu cần thiết để trả về ước tính chính xác hơn cho mỗi hàng.

5. (Nếu cần) Thêm bộ đệm chiều cao hàng

Nếu bạn đã thực hiện tất cả các cách trên và vẫn thấy rằng hiệu suất chậm đến mức không thể chấp nhận được khi thực hiện giải quyết ràng buộc tableView:heightForRowAtIndexPath:, bạn sẽ không may phải thực hiện một số bộ đệm cho chiều cao của ô. (Đây là cách tiếp cận được đề xuất bởi các kỹ sư của Apple.) Ý tưởng chung là để công cụ Autolayout giải quyết các ràng buộc lần đầu tiên, sau đó lưu bộ đệm chiều cao tính toán cho ô đó và sử dụng giá trị được lưu trong bộ đệm cho tất cả các yêu cầu trong tương lai cho chiều cao của ô đó. Thủ thuật tất nhiên là đảm bảo bạn xóa chiều cao được lưu trong bộ nhớ cache khi có bất cứ điều gì xảy ra có thể khiến chiều cao của ô thay đổi - chủ yếu, đó là khi nội dung của ô đó thay đổi hoặc khi các sự kiện quan trọng khác xảy ra (như người dùng điều chỉnh thanh trượt kích thước văn bản Kiểu động).

Mã mẫu chung của iOS 7 (có nhiều bình luận thú vị)

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    // Determine which reuse identifier should be used for the cell at this 
    // index path, depending on the particular layout required (you may have
    // just one, or may have many).
    NSString *reuseIdentifier = ...;

    // Dequeue a cell for the reuse identifier.
    // Note that this method will init and return a new cell if there isn't
    // one available in the reuse pool, so either way after this line of 
    // code you will have a cell with the correct constraints ready to go.
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:reuseIdentifier];

    // Configure the cell with content for the given indexPath, for example:
    // cell.textLabel.text = someTextForThisCell;
    // ...

    // Make sure the constraints have been set up for this cell, since it 
    // may have just been created from scratch. Use the following lines, 
    // assuming you are setting up constraints from within the cell's 
    // updateConstraints method:
    [cell setNeedsUpdateConstraints];
    [cell updateConstraintsIfNeeded];

    // If you are using multi-line UILabels, don't forget that the 
    // preferredMaxLayoutWidth needs to be set correctly. Do it at this 
    // point if you are NOT doing it within the UITableViewCell subclass 
    // -[layoutSubviews] method. For example: 
    // cell.multiLineLabel.preferredMaxLayoutWidth = CGRectGetWidth(tableView.bounds);

    return cell;
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    // Determine which reuse identifier should be used for the cell at this 
    // index path.
    NSString *reuseIdentifier = ...;

    // Use a dictionary of offscreen cells to get a cell for the reuse 
    // identifier, creating a cell and storing it in the dictionary if one 
    // hasn't already been added for the reuse identifier. WARNING: Don't 
    // call the table view's dequeueReusableCellWithIdentifier: method here 
    // because this will result in a memory leak as the cell is created but 
    // never returned from the tableView:cellForRowAtIndexPath: method!
    UITableViewCell *cell = [self.offscreenCells objectForKey:reuseIdentifier];
    if (!cell) {
        cell = [[YourTableViewCellClass alloc] init];
        [self.offscreenCells setObject:cell forKey:reuseIdentifier];
    }

    // Configure the cell with content for the given indexPath, for example:
    // cell.textLabel.text = someTextForThisCell;
    // ...

    // Make sure the constraints have been set up for this cell, since it 
    // may have just been created from scratch. Use the following lines, 
    // assuming you are setting up constraints from within the cell's 
    // updateConstraints method:
    [cell setNeedsUpdateConstraints];
    [cell updateConstraintsIfNeeded];

    // Set the width of the cell to match the width of the table view. This
    // is important so that we'll get the correct cell height for different
    // table view widths if the cell's height depends on its width (due to 
    // multi-line UILabels word wrapping, etc). We don't need to do this 
    // above in -[tableView:cellForRowAtIndexPath] because it happens 
    // automatically when the cell is used in the table view. Also note, 
    // the final width of the cell may not be the width of the table view in
    // some cases, for example when a section index is displayed along 
    // the right side of the table view. You must account for the reduced 
    // cell width.
    cell.bounds = CGRectMake(0.0, 0.0, CGRectGetWidth(tableView.bounds), CGRectGetHeight(cell.bounds));

    // Do the layout pass on the cell, which will calculate the frames for 
    // all the views based on the constraints. (Note that you must set the 
    // preferredMaxLayoutWidth on multiline UILabels inside the 
    // -[layoutSubviews] method of the UITableViewCell subclass, or do it 
    // manually at this point before the below 2 lines!)
    [cell setNeedsLayout];
    [cell layoutIfNeeded];

    // Get the actual height required for the cell's contentView
    CGFloat height = [cell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;

    // Add an extra point to the height to account for the cell separator, 
    // which is added between the bottom of the cell's contentView and the 
    // bottom of the table view cell.
    height += 1.0;

    return height;
}

// NOTE: Set the table view's estimatedRowHeight property instead of 
// implementing the below method, UNLESS you have extreme variability in 
// your row heights and you notice the scroll indicator "jumping" 
// as you scroll.
- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    // Do the minimal calculations required to be able to return an 
    // estimated row height that's within an order of magnitude of the 
    // actual height. For example:
    if ([self isTallCellAtIndexPath:indexPath]) {
        return 350.0;
    } else {
        return 40.0;
    }
}

Dự án mẫu

Các dự án này là các ví dụ hoạt động đầy đủ về chế độ xem bảng với độ cao hàng thay đổi do các ô xem bảng có chứa nội dung động trong UILabels.

Xamarin (C # /. NET)

Nếu bạn đang sử dụng Xamarin, hãy xem dự án mẫu này được kết hợp bởi @KentBoogaart .


1
Mặc dù điều này đang hoạt động tốt, tôi thấy nó theo ước tính kích thước yêu cầu một chút (có thể do các vấn đề làm tròn khác nhau) và tôi phải thêm một vài điểm vào chiều cao cuối cùng để tất cả văn bản của tôi nằm
gọn

5
@ Alex311 Rất thú vị, cảm ơn vì đã cung cấp ví dụ này. Tôi đã làm một thử nghiệm nhỏ về kết thúc của mình và viết lên một số bình luận ở đây: github.com/Alex311/TableCellWithAutoLayout/commit/
trộm

3
Tôi thực sự khuyên bạn nên lưu vào bộ đệm cho từng loại định danh sử dụng lại ô. Mỗi lần bạn cân bằng chiều cao, bạn sẽ lấy một ô ra khỏi hàng đợi không được thêm lại. Bộ nhớ đệm có thể giảm đáng kể số lần bộ khởi tạo cho các ô trong bảng của bạn được gọi. Vì một số lý do, khi tôi không lưu vào bộ nhớ cache, lượng bộ nhớ được sử dụng sẽ tăng lên khi tôi cuộn.
Alex311

1
Bảng phân cảnh, thực sự. Tôi không nghĩ rằng nó hoạt động cho các tế bào nguyên mẫu (ít nhất là không khởi tạo lại toàn bộ VC). Có thể tránh được sự rò rỉ hoàn toàn bằng cách khử trong heightForRowAtIndexPath, giữ tế bào và trả lại lần sau khi cellForRowAtIndexPathđược gọi.
nschum

2
Việc iOS8triển khai vẫn được khuyến nghị iOS9iOS10, hoặc có những cách tiếp cận mới kể từ khi câu trả lời này được công bố?
koen

175

Đối với iOS 8 ở trên, nó thực sự đơn giản:

override func viewDidLoad() {  
    super.viewDidLoad()

    self.tableView.estimatedRowHeight = 80
    self.tableView.rowHeight = UITableView.automaticDimension
}

hoặc là

func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
    return UITableView.automaticDimension
}

Nhưng đối với iOS 7, chìa khóa là tính chiều cao sau khi tự động thoát:

func calculateHeightForConfiguredSizingCell(cell: GSTableViewCell) -> CGFloat {
    cell.setNeedsLayout()
    cell.layoutIfNeeded()
    let height = cell.contentView.systemLayoutSizeFittingSize(UILayoutFittingExpandedSize).height + 1.0
    return height
}

Quan trọng

  • Nếu nhiều dòng nhãn, đừng quên thiết lập numberOfLinesđể 0.

  • Đừng quên label.preferredMaxLayoutWidth = CGRectGetWidth(tableView.bounds)

Mã ví dụ đầy đủ ở đây .


7
Tôi nghĩ rằng chúng ta không cần phải thực hiện chức năng heightForRowAtIndexPath trong trường hợp OS8
jpulikkottil 16/07/2015

Như @eddwinpaz lưu ý về một câu trả lời khác, điều quan trọng là các ràng buộc được sử dụng để khóa chiều cao hàng KHÔNG bao gồm lề.
Richard

1
+1 cho "Nếu nhiều nhãn dòng, đừng quên đặt numberOfLines thành 0", điều này khiến các ô của tôi không có kích thước động.
Ian-Fogelman

Gọi tôi là người thích kiểm soát nhưng ngay cả trong thời của iOS 11, tôi vẫn không thích sử dụng UITableViewAutomaticDimension. Có một số kinh nghiệm xấu với nó trong quá khứ. Do đó, tôi thường sử dụng các giải pháp iOS7 được liệt kê ở đây. Lưu ý của William đừng quên nhãn.preferredMaxLayoutWidth ở đây đã cứu tôi.
Brian Sachetta

Khi chúng tôi cuộn, nhãn bắt đầu xuất hiện ở nhiều dòng và chiều cao của hàng cũng không tăng
Karanveer Singh

93

Ví dụ nhanh về chiều cao thay đổi UITableViewCell

Đã cập nhật cho Swift 3

Câu trả lời Swift của William Hu là tốt, nhưng nó giúp tôi có một số bước đơn giản nhưng chi tiết khi học cách làm điều gì đó lần đầu tiên. Ví dụ dưới đây là dự án thử nghiệm của tôi trong khi học cách tạo ra một UITableViewchiều cao ô thay đổi. Tôi dựa trên ví dụ UITableView cơ bản này cho Swift .

Dự án đã hoàn thành sẽ trông như thế này:

nhập mô tả hình ảnh ở đây

Tạo một dự án mới

Nó có thể chỉ là một ứng dụng xem đơn.

Thêm mã

Thêm một tệp Swift mới vào dự án của bạn. Đặt tên là MyCustomCell. Lớp này sẽ giữ các cửa hàng cho các khung nhìn mà bạn thêm vào ô của mình trong bảng phân cảnh. Trong ví dụ cơ bản này, chúng ta sẽ chỉ có một nhãn trong mỗi ô.

import UIKit
class MyCustomCell: UITableViewCell {
    @IBOutlet weak var myCellLabel: UILabel!
}

Chúng tôi sẽ kết nối ổ cắm này sau.

Mở ViewControll.swift và đảm bảo bạn có nội dung sau:

import UIKit
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {

    // These strings will be the data for the table view cells
    let animals: [String] = [
        "Ten horses:  horse horse horse horse horse horse horse horse horse horse ",
        "Three cows:  cow, cow, cow",
        "One camel:  camel",
        "Ninety-nine sheep:  sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep baaaa sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep",
        "Thirty goats:  goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat "]

    // Don't forget to enter this in IB also
    let cellReuseIdentifier = "cell"

    @IBOutlet var tableView: UITableView!

    override func viewDidLoad() {
        super.viewDidLoad()

        // delegate and data source
        tableView.delegate = self
        tableView.dataSource = self

        // Along with auto layout, these are the keys for enabling variable cell height
        tableView.estimatedRowHeight = 44.0
        tableView.rowHeight = UITableViewAutomaticDimension
    }

    // number of rows in table view
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return self.animals.count
    }

    // create a cell for each table view row
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

        let cell:MyCustomCell = self.tableView.dequeueReusableCell(withIdentifier: cellReuseIdentifier) as! MyCustomCell
        cell.myCellLabel.text = self.animals[indexPath.row]
        return cell
    }

    // method to run when table view cell is tapped
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        print("You tapped cell number \(indexPath.row).")
    }
}

Lưu ý quan trọng:

  • Đây là hai dòng mã sau (cùng với bố cục tự động) làm cho chiều cao ô biến đổi có thể:

    tableView.estimatedRowHeight = 44.0
    tableView.rowHeight = UITableViewAutomaticDimension

Thiết lập bảng phân cảnh

Thêm Chế độ xem bảng vào trình điều khiển chế độ xem của bạn và sử dụng bố cục tự động để ghim nó vào bốn phía. Sau đó kéo một ô xem bảng vào chế độ xem bảng. Và vào ô Prototype, kéo Nhãn. Sử dụng bố cục tự động để ghim nhãn vào bốn cạnh của chế độ xem nội dung của Ô xem bảng.

nhập mô tả hình ảnh ở đây

Lưu ý quan trọng:

  • Bố cục tự động hoạt động cùng với hai dòng mã quan trọng tôi đã đề cập ở trên. Nếu bạn không sử dụng bố trí tự động, nó sẽ không hoạt động.

Cài đặt IB khác

Tên lớp và định danh tùy chỉnh

Chọn Ô xem bảng và đặt lớp tùy chỉnh thành MyCustomCell(tên của lớp trong tệp Swift mà chúng tôi đã thêm). Đồng thời đặt Mã định danh là cell(cùng chuỗi mà chúng tôi đã sử dụng cho cellReuseIdentifiermã ở trên.

nhập mô tả hình ảnh ở đây

Dòng không cho nhãn

Đặt số lượng dòng 0trong Nhãn của bạn. Điều này có nghĩa là đa dòng và cho phép nhãn thay đổi kích thước dựa trên nội dung của nó.

nhập mô tả hình ảnh ở đây

Kết nối các cửa hàng

  • Điều khiển kéo từ Chế độ xem bảng trong bảng phân cảnh đến tableViewbiến trong ViewControllermã.
  • Thực hiện tương tự cho Nhãn trong ô Nguyên mẫu của bạn với myCellLabelbiến trong MyCustomCelllớp.

Đã kết thúc

Bạn sẽ có thể chạy dự án của bạn bây giờ và có được các ô có chiều cao thay đổi.

Ghi chú

  • Ví dụ này chỉ hoạt động cho iOS 8 trở đi. Nếu bạn vẫn cần hỗ trợ iOS 7 thì điều này sẽ không phù hợp với bạn.
  • Các ô tùy chỉnh của riêng bạn trong các dự án tương lai của bạn có thể sẽ có nhiều hơn một nhãn. Đảm bảo rằng bạn nhận được mọi thứ được ghim đúng để bố trí tự động có thể xác định chiều cao chính xác để sử dụng. Bạn cũng có thể phải sử dụng kháng nén dọc và ôm. Xem bài viết này để biết thêm về điều đó.
  • Nếu bạn không ghim các cạnh đầu và cuối (trái và phải), bạn cũng có thể cần đặt nhãn preferredMaxLayoutWidthđể nó biết khi nào cần bọc đường. Ví dụ: nếu bạn đã thêm một ràng buộc Trung tâm theo chiều ngang cho nhãn trong dự án ở trên thay vì ghim các cạnh đầu và cuối, thì bạn sẽ cần thêm dòng này vào tableView:cellForRowAtIndexPathphương thức:

     cell.myCellLabel.preferredMaxLayoutWidth = tableView.bounds.width

Xem thêm


Có thực sự tốt hơn để đặt preferredMaxLayoutWidthchống lại tế bào contentSizekhông? Theo cách đó, nếu bạn có một phụ kiện Xem hoặc nó đã được vuốt để chỉnh sửa thì nó vẫn sẽ được xem xét?
Mật ong

@Honey, bạn rất tốt có thể đúng. Tôi đã không theo kịp iOS khoảng một năm nay và tôi quá không thể trả lời tốt cho bạn ngay bây giờ.
Suragch

65

Tôi đã gói giải pháp iOS7 của @ smileyborg trong một danh mục

Tôi quyết định bọc giải pháp thông minh này của @smileyborg vào một UICollectionViewCell+AutoLayoutDynamicHeightCalculationdanh mục.

Danh mục cũng khắc phục các vấn đề được nêu trong câu trả lời của @ wildmonkey (tải một ô từ ngòi và systemLayoutSizeFittingSize:trở về CGRectZero)

Nó không tính đến bất kỳ bộ nhớ đệm nào nhưng phù hợp với nhu cầu của tôi ngay bây giờ. Hãy sao chép, dán và hack vào nó.

UICollectionViewCell + AutoLayoutDocateHeightCalculation.h

#import <UIKit/UIKit.h>

typedef void (^UICollectionViewCellAutoLayoutRenderBlock)(void);

/**
 *  A category on UICollectionViewCell to aid calculating dynamic heights based on AutoLayout contraints.
 *
 *  Many thanks to @smileyborg and @wildmonkey
 *
 *  @see stackoverflow.com/questions/18746929/using-auto-layout-in-uitableview-for-dynamic-cell-layouts-variable-row-heights
 */
@interface UICollectionViewCell (AutoLayoutDynamicHeightCalculation)

/**
 *  Grab an instance of the receiving type to use in order to calculate AutoLayout contraint driven dynamic height. The method pulls the cell from a nib file and moves any Interface Builder defined contrainsts to the content view.
 *
 *  @param name Name of the nib file.
 *
 *  @return collection view cell for using to calculate content based height
 */
+ (instancetype)heightCalculationCellFromNibWithName:(NSString *)name;

/**
 *  Returns the height of the receiver after rendering with your model data and applying an AutoLayout pass
 *
 *  @param block Render the model data to your UI elements in this block
 *
 *  @return Calculated constraint derived height
 */
- (CGFloat)heightAfterAutoLayoutPassAndRenderingWithBlock:(UICollectionViewCellAutoLayoutRenderBlock)block collectionViewWidth:(CGFloat)width;

/**
 *  Directly calls `heightAfterAutoLayoutPassAndRenderingWithBlock:collectionViewWidth` assuming a collection view width spanning the [UIScreen mainScreen] bounds
 */
- (CGFloat)heightAfterAutoLayoutPassAndRenderingWithBlock:(UICollectionViewCellAutoLayoutRenderBlock)block;

@end

UICollectionViewCell + AutoLayoutDocateHeightCalculation.m

#import "UICollectionViewCell+AutoLayout.h"

@implementation UICollectionViewCell (AutoLayout)

#pragma mark Dummy Cell Generator

+ (instancetype)heightCalculationCellFromNibWithName:(NSString *)name
{
    UICollectionViewCell *heightCalculationCell = [[[NSBundle mainBundle] loadNibNamed:name owner:self options:nil] lastObject];
    [heightCalculationCell moveInterfaceBuilderLayoutConstraintsToContentView];
    return heightCalculationCell;
}

#pragma mark Moving Constraints

- (void)moveInterfaceBuilderLayoutConstraintsToContentView
{
    [self.constraints enumerateObjectsUsingBlock:^(NSLayoutConstraint *constraint, NSUInteger idx, BOOL *stop) {
        [self removeConstraint:constraint];
        id firstItem = constraint.firstItem == self ? self.contentView : constraint.firstItem;
        id secondItem = constraint.secondItem == self ? self.contentView : constraint.secondItem;
        [self.contentView addConstraint:[NSLayoutConstraint constraintWithItem:firstItem
                                                                     attribute:constraint.firstAttribute
                                                                     relatedBy:constraint.relation
                                                                        toItem:secondItem
                                                                     attribute:constraint.secondAttribute
                                                                    multiplier:constraint.multiplier
                                                                      constant:constraint.constant]];
    }];
}

#pragma mark Height

- (CGFloat)heightAfterAutoLayoutPassAndRenderingWithBlock:(UICollectionViewCellAutoLayoutRenderBlock)block
{
    return [self heightAfterAutoLayoutPassAndRenderingWithBlock:block
                                            collectionViewWidth:CGRectGetWidth([[UIScreen mainScreen] bounds])];
}

- (CGFloat)heightAfterAutoLayoutPassAndRenderingWithBlock:(UICollectionViewCellAutoLayoutRenderBlock)block collectionViewWidth:(CGFloat)width
{
    NSParameterAssert(block);

    block();

    [self setNeedsUpdateConstraints];
    [self updateConstraintsIfNeeded];

    self.bounds = CGRectMake(0.0f, 0.0f, width, CGRectGetHeight(self.bounds));

    [self setNeedsLayout];
    [self layoutIfNeeded];

    CGSize calculatedSize = [self.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize];

    return calculatedSize.height;

}

@end

Ví dụ sử dụng:

- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
{
    MYSweetCell *cell = [MYSweetCell heightCalculationCellFromNibWithName:NSStringFromClass([MYSweetCell class])];
    CGFloat height = [cell heightAfterAutoLayoutPassAndRenderingWithBlock:^{
        [(id<MYSweetCellRenderProtocol>)cell renderWithModel:someModel];
    }];
    return CGSizeMake(CGRectGetWidth(self.collectionView.bounds), height);
}

Rất may, chúng tôi sẽ không phải chơi nhạc jazz này trong iOS8, nhưng bây giờ nó đã có!


2
Bạn chỉ có thể sử dụng một: [YourCell new]và sử dụng nó như là hình nộm. Miễn là mã xây dựng mã ràng buộc được kích hoạt trong trường hợp của bạn và bạn kích hoạt một bố cục vượt qua theo chương trình, bạn sẽ thấy ổn.
Adam Chờ đợi

1
Cảm ơn! Những công việc này. Thể loại của bạn là tuyệt vời. Đó là những gì làm cho tôi nhận ra rằng kỹ thuật này cũng làm việc với UICollectionViews.
Ricardo Sanchez-Saez

2
Làm thế nào bạn sẽ làm điều này bằng cách sử dụng một tế bào nguyên mẫu được xác định trong bảng phân cảnh?
David Potter

57

Đây là giải pháp của tôi:

Bạn cần phải nói với TableViewcác estimatedHeighttrước khi nó tải xem. Nếu không, nó sẽ không thể cư xử như mong đợi.

Mục tiêu-C

- (void)viewWillAppear:(BOOL)animated {
    _messageField.delegate = self;
    _tableView.estimatedRowHeight = 65.0;
    _tableView.rowHeight = UITableViewAutomaticDimension;
}

Cập nhật lên Swift 4.2

override func viewWillAppear(_ animated: Bool) {
    tableView.rowHeight = UITableView.automaticDimension
    tableView.estimatedRowHeight = 65.0
}

2
Thiết lập tự động thanh toán chính xác, cùng với mã này được thêm vào trong viewDidLoad đã thực hiện thủ thuật này.
Bruce

1
Nhưng nếu estimatedRowHeightthay đổi theo từng hàng thì sao? Tôi nên hơn hoặc theo ước tính? sử dụng tối thiểu hoặc tối đa chiều cao tôi sử dụng tableView?
János

1
@ János đây là điểm của rowHeight. Để thực hiện điều này như mong đợi, bạn cần sử dụng các ràng buộc không có lề và căn chỉnh với TableViewCell các đối tượng. và tôi cho rằng bạn đang sử dụng UITextView nên bạn vẫn cần xóa autoscroll = false nếu không nó sẽ duy trì chiều cao và chiều cao tương đối sẽ không hoạt động như mong đợi.
Eddwin Paz

1
Đó là giải pháp mạnh mẽ nhất. Đừng lo lắng estimatedRowHeight, nó chủ yếu ảnh hưởng đến kích thước của thanh cuộn, không bao giờ là chiều cao của các ô thực tế. Hãy in đậm theo chiều cao bạn chọn: nó sẽ ảnh hưởng đến hoạt hình chèn / xóa.
SwiftArchitect

Đây là mã mẫu trong swift 2.3 github.com/dpakthakur/DAVECellHeight
Deepak Thakur

47

Giải pháp được đề xuất bởi @smileyborg gần như hoàn hảo. Nếu bạn có một tế bào tùy chỉnh và bạn muốn một hoặc nhiều UILabelvới chiều cao năng động thì systemLayoutSizeFittingSize phương pháp kết hợp với AutoLayout kích hoạt trở lại một CGSizeZero, trừ khi bạn di chuyển tất cả những hạn chế di động của bạn từ các tế bào để contentView của nó (theo đề nghị của @TomSwift đây Làm thế nào để thay đổi kích thước SuperView để phù hợp với tất cả các cuộc phỏng vấn với autolayout? ).

Để làm như vậy, bạn cần chèn đoạn mã sau vào triển khai UITableViewCell tùy chỉnh của mình (nhờ @Adrian).

- (void)awakeFromNib{
    [super awakeFromNib];
    for (NSLayoutConstraint *cellConstraint in self.constraints) {
        [self removeConstraint:cellConstraint];
        id firstItem = cellConstraint.firstItem == self ? self.contentView : cellConstraint.firstItem;
        id seccondItem = cellConstraint.secondItem == self ? self.contentView : cellConstraint.secondItem;
        NSLayoutConstraint *contentViewConstraint =
        [NSLayoutConstraint constraintWithItem:firstItem
                                 attribute:cellConstraint.firstAttribute
                                 relatedBy:cellConstraint.relation
                                    toItem:seccondItem
                                 attribute:cellConstraint.secondAttribute
                                multiplier:cellConstraint.multiplier
                                  constant:cellConstraint.constant];
        [self.contentView addConstraint:contentViewConstraint];
    }
}

Trộn @smileyborg trả lời với điều này sẽ hoạt động.


systemLayoutSizeFittingSizecần được gọi trên contentView, không phải cell
onmyway133

25

Một gotcha đủ quan trọng tôi vừa chạy vào để đăng như một câu trả lời.

Câu trả lời của @ smileyborg là chính xác. Tuy nhiên, nếu bạn có bất kỳ mã nào trong layoutSubviewsphương thức của lớp ô tùy chỉnh của mình, ví dụ như đặt cài đặt preferredMaxLayoutWidth, thì nó sẽ không được chạy với mã này:

[cell.contentView setNeedsLayout];
[cell.contentView layoutIfNeeded];

Nó làm tôi bối rối một lúc. Sau đó, tôi nhận ra điều đó bởi vì những điều đó chỉ kích hoạt layoutSubview trêncontentView chứ không phải chính tế bào.

Mã làm việc của tôi trông như thế này:

TCAnswerDetailAppSummaryCell *cell = [self.tableView dequeueReusableCellWithIdentifier:@"TCAnswerDetailAppSummaryCell"];
[cell configureWithThirdPartyObject:self.app];
[cell layoutIfNeeded];
CGFloat height = [cell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;
return height;

Lưu ý rằng nếu bạn đang tạo một ô mới, tôi khá chắc chắn rằng bạn không cần phải gọi setNeedsLayout vì nó đã được đặt. Trong trường hợp bạn lưu tham chiếu đến một ô, có lẽ bạn nên gọi nó. Dù bằng cách nào nó cũng không làm tổn thương gì.

Một mẹo khác nếu bạn đang sử dụng các lớp con ô nơi bạn đang thiết lập những thứ như thế preferredMaxLayoutWidth. Như @smileyborg đề cập, "ô xem bảng của bạn chưa có chiều rộng cố định với chiều rộng của chế độ xem bảng". Điều này là đúng và rắc rối nếu bạn đang thực hiện công việc của mình trong lớp con chứ không phải trong trình điều khiển xem. Tuy nhiên, bạn có thể chỉ cần đặt khung ô tại thời điểm này bằng chiều rộng bảng:

Ví dụ trong tính toán cho chiều cao:

self.summaryCell = [self.tableView dequeueReusableCellWithIdentifier:@"TCAnswerDetailDefaultSummaryCell"];
CGRect oldFrame = self.summaryCell.frame;
self.summaryCell.frame = CGRectMake(oldFrame.origin.x, oldFrame.origin.y, self.tableView.frame.size.width, oldFrame.size.height);

(Tôi tình cờ lưu trữ tế bào đặc biệt này để sử dụng lại, nhưng điều đó không liên quan).



19

Miễn là bố trí của bạn trong tế bào của bạn là tốt.

-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = [self tableView:tableView cellForRowAtIndexPath:indexPath];

    return [cell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;
}

Cập nhật: Bạn nên sử dụng thay đổi kích thước động được giới thiệu trong iOS 8.


Đây là hoạt động đối với tôi trên iOS7, là nó bây giờ OK để gọi tableView:cellForRowAtIndexPath:trong tableView:heightForRowAtIndexPath:lúc này?
Kiến

Ok vì vậy đây không hoạt động, nhưng khi tôi gọi systemLayoutSizeFittingSize:trong tableView:cellForRowAtIndexPath:và bộ nhớ cache kết quả sau đó và sau đó sử dụng trong tableView:heightForRowAtIndexPath:nó hoạt động tốt chừng nào hạn chế được thiết lập một cách chính xác tất nhiên!
Kiến

Điều này chỉ hoạt động nếu bạn sử dụng dequeueReabilitiesCellWithIdentifier: thay vì dequeueReabilitiesCellWithIdentifier: forIndexPath:
Antoine

1
Tôi thực sự không nghĩ rằng gọi bảngView: cellForRowAtIndexPath: trực tiếp là một cách tốt.
Itachi

19

(đối với Xcode 8.x / Xcode 9.x đọc ở phía dưới)

Cảnh giác với vấn đề sau trong Xcode 7.x, đây có thể là một nguồn gây nhầm lẫn:

Interface Builder không xử lý thiết lập ô tự động đúng cách. Ngay cả khi các ràng buộc của bạn là hoàn toàn hợp lệ, IB vẫn sẽ khiếu nại và đưa ra cho bạn các đề xuất và lỗi khó hiểu. Lý do là IB không sẵn sàng thay đổi chiều cao của hàng khi các ràng buộc của bạn sai khiến (để ô phù hợp với nội dung của bạn). Thay vào đó, nó giữ chiều cao của hàng cố định và bắt đầu đề nghị bạn thay đổi các ràng buộc của mình, điều mà bạn nên bỏ qua .

Ví dụ, hãy tưởng tượng bạn đã thiết lập mọi thứ tốt đẹp, không có cảnh báo, không có lỗi, tất cả đều hoạt động.

nhập mô tả hình ảnh ở đây

Bây giờ nếu bạn thay đổi kích thước phông chữ (trong ví dụ này, tôi sẽ thay đổi kích thước phông chữ nhãn mô tả từ 17.0 đến 18.0).

nhập mô tả hình ảnh ở đây

Vì kích thước phông chữ tăng lên, nhãn bây giờ muốn chiếm 3 hàng (trước đó nó đã chiếm 2 hàng).

Nếu Trình tạo giao diện hoạt động như mong đợi, nó sẽ thay đổi kích thước chiều cao của ô để phù hợp với chiều cao nhãn mới. Tuy nhiên, điều thực sự xảy ra là IB hiển thị biểu tượng lỗi bố cục tự động màu đỏ và đề nghị bạn sửa đổi các ưu tiên ôm / nén.

nhập mô tả hình ảnh ở đây

Bạn nên bỏ qua những cảnh báo này. Thay vào đó, những gì bạn có thể * làm là thay đổi thủ công chiều cao của hàng trong (chọn Ô> Trình kiểm tra kích thước> Chiều cao hàng).

nhập mô tả hình ảnh ở đây

Tôi đã thay đổi độ cao này một lần nhấp (sử dụng bước tăng / giảm) cho đến khi các lỗi mũi tên màu đỏ biến mất! (bạn thực sự sẽ nhận được các cảnh báo màu vàng, tại thời điểm này chỉ cần tiếp tục và thực hiện 'cập nhật khung', tất cả sẽ hoạt động).

* Lưu ý rằng bạn thực sự không phải giải quyết các lỗi màu đỏ hoặc cảnh báo màu vàng này trong Trình tạo giao diện - khi chạy, mọi thứ sẽ hoạt động chính xác (ngay cả khi IB hiển thị lỗi / cảnh báo). Chỉ cần đảm bảo rằng trong thời gian chạy trong nhật ký giao diện điều khiển, bạn sẽ không gặp phải bất kỳ lỗi AutoLayout nào.

Trong thực tế, cố gắng luôn cập nhật chiều cao hàng trong IB là cực kỳ khó chịu và đôi khi gần như không thể (vì các giá trị phân số).

Để ngăn chặn những lời cảnh báo IB gây phiền nhiễu / lỗi, bạn có thể chọn chế độ xem liên quan và trong Size Inspectorcho bất động sản AmbiguitychọnVerify Position Only

nhập mô tả hình ảnh ở đây


Xcode 8.x / Xcode 9.x dường như (đôi khi) đang làm những việc khác với Xcode 7.x, nhưng vẫn không chính xác. Ví dụ: ngay cả khi compression resistance priority/ hugging priorityđược đặt thành bắt buộc (1000), Trình tạo giao diện có thể kéo dài hoặc cắt nhãn để vừa với ô (thay vì thay đổi kích thước chiều cao của ô để vừa với nhãn). Và trong trường hợp như vậy, nó thậm chí có thể không hiển thị bất kỳ cảnh báo hoặc lỗi AutoLayout nào. Hoặc đôi khi nó thực hiện chính xác những gì Xcode 7.x đã làm, được mô tả ở trên.


hi có thể đưa ra chiều cao động cho ô không, có chế độ xem bảng với ô có nội dung ô động.?
Jignesh B

18

Để đặt kích thước tự động cho chiều cao hàng & chiều cao hàng ước tính, hãy đảm bảo các bước sau để thực hiện, kích thước tự động có hiệu quả đối với bố trí chiều cao ô / hàng.

  • Chỉ định và triển khai dữ liệu xem bảng Nguồn và ủy quyền
  • Chỉ định UITableViewAutomaticDimensioncho rowHeight & ước tínhRowHeight
  • Thực hiện các phương thức ủy nhiệm / dataSource (nghĩa là heightForRowAttrả về một giá trị UITableViewAutomaticDimensioncho nó)

-

Mục tiêu C:

// in ViewController.h
#import <UIKit/UIKit.h>

@interface ViewController : UIViewController <UITableViewDelegate, UITableViewDataSource>

  @property IBOutlet UITableView * table;

@end

// in ViewController.m

- (void)viewDidLoad {
    [super viewDidLoad];
    self.table.dataSource = self;
    self.table.delegate = self;

    self.table.rowHeight = UITableViewAutomaticDimension;
    self.table.estimatedRowHeight = UITableViewAutomaticDimension;
}

-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {

    return UITableViewAutomaticDimension;
}

Nhanh:

@IBOutlet weak var table: UITableView!

override func viewDidLoad() {
    super.viewDidLoad()

    // Don't forget to set dataSource and delegate for table
    table.dataSource = self
    table.delegate = self

    // Set automatic dimensions for row height
    // Swift 4.2 onwards
    table.rowHeight = UITableView.automaticDimension
    table.estimatedRowHeight = UITableView.automaticDimension


    // Swift 4.1 and below
    table.rowHeight = UITableViewAutomaticDimension
    table.estimatedRowHeight = UITableViewAutomaticDimension

}



// UITableViewAutomaticDimension calculates height of label contents/text
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
    // Swift 4.2 onwards
    return UITableView.automaticDimension

    // Swift 4.1 and below
    return UITableViewAutomaticDimension
}

Ví dụ nhãn trong UITableviewCell

  • Đặt số lượng dòng = 0 (& chế độ ngắt dòng = cắt đuôi)
  • Đặt tất cả các ràng buộc (trên cùng, dưới cùng, bên trái) đối với thùng chứa giám sát / ô của nó.
  • Tùy chọn : Đặt chiều cao tối thiểu cho nhãn, nếu bạn muốn diện tích dọc tối thiểu được bao phủ bởi nhãn, ngay cả khi không có dữ liệu.

nhập mô tả hình ảnh ở đây

Lưu ý : Nếu bạn có nhiều hơn một nhãn (UIElements) có độ dài động, cần điều chỉnh theo kích thước nội dung của nó: Điều chỉnh 'Ưu tiên ôm và nén nội dung ưu tiên' cho các nhãn mà bạn muốn mở rộng / nén với mức độ ưu tiên cao hơn.


1
Cảm ơn bạn có vẻ như là một giải pháp rõ ràng và đơn giản nhưng tôi có một vấn đề tôi không sử dụng nhãn mà là xem xét văn bản vì vậy tôi cần chiều cao hàng để tăng khi dữ liệu được thêm vào. Vấn đề của tôi sau đó là chuyển thông tin đến heightForRowAt. Tôi có thể đo chiều cao của các bài phỏng vấn thay đổi của mình nhưng bây giờ cần thay đổi chiều cao của hàng. Xin đánh giá cao sự giúp đỡ nào đó
Jeremy Andrew

@JeremyAndrews Chắc chắn sẽ giúp bạn. Đặt câu hỏi của bạn với mã nguồn bạn đã thử và chi tiết về vấn đề.
Krunal

Nó làm việc cho tôi mà không cần triển khai tableView: heightForRownguồn dữ liệu.
Hemang

@Hemang - Vì iOS 11+ nên nó hoạt động mà không có tableView: heightForRow. (đối với iOS 10- tableView: heightForRowlà bắt buộc)
Krunal

1
@Hemang - Giải pháp phụ thuộc vào định nghĩa truy vấn chính xác. Ở đây tôi đã có giải pháp chung cho truy vấn của bạn. Đặt điều kiện bên trong tableView: heightForRow..if (indexPath.row == 0) { return 100} else { return UITableView.automaticDimension }
Krunal

16

Giống như @ Bob-Spryn, tôi đã gặp một vấn đề quan trọng đủ để tôi đăng bài này như một câu trả lời.

Tôi vật lộn với câu trả lời của @ smileyborg một lúc. Gotcha mà tôi chạy vào là nếu bạn đã xác định tế bào nguyên mẫu của bạn trong IB với các yếu tố bổ sung ( UILabels, UIButtons, vv) trong IB khi bạn nhanh chóng các tế bào với [ [YourTableViewCellClass alloc] init]nó sẽ không nhanh chóng tất cả các yếu tố khác trong tế bào đó trừ khi bạn đã viết mã để làm điều đó. (Tôi đã có một trải nghiệm tương tự với initWithStyle.)

Để bảng phân cảnh khởi tạo tất cả các yếu tố bổ sung có được ô của bạn [tableView dequeueReusableCellWithIdentifier:@"DoseNeeded"](Không phải [tableView dequeueReusableCellWithIdentifier:forIndexPath:]vì điều này sẽ gây ra sự cố thú vị.) Khi bạn thực hiện việc này, tất cả các yếu tố bạn đã xác định trong IB sẽ được khởi tạo.


15

Bảng động Xem chiều cao ô và bố trí tự động

Một cách tốt để giải quyết vấn đề với Giao diện tự động Bố cục:

- (CGFloat)heightForImageCellAtIndexPath:(NSIndexPath *)indexPath {
  static RWImageCell *sizingCell = nil;
  static dispatch_once_t onceToken;
  dispatch_once(&onceToken, ^{
    sizingCell = [self.tableView dequeueReusableCellWithIdentifier:RWImageCellIdentifier];
  });

  [sizingCell setNeedsLayout];
  [sizingCell layoutIfNeeded];

  CGSize size = [sizingCell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize];
  return size.height;
}

1
Điều này đã được bao quát rộng rãi trong câu trả lời được chấp nhận cho câu hỏi này.
smileyborg

2
Vâng, tôi biết ... nhưng tôi không muốn sử dụng PureLayout và công cụ 'lừa đảo' đã giúp tôi rất nhiều, để giải quyết nó chỉ bằng cách sử dụng Storyboard.
Mohnasm

13
tableView.estimatedRowHeight = 343.0
tableView.rowHeight = UITableViewAutomaticDimension

nhập mô tả hình ảnh ở đây


13

Một "giải pháp" khác: bỏ qua tất cả sự thất vọng này và sử dụng UIScrollView thay vào đó để có được kết quả trông giống và cảm thấy giống hệt với UITableView.

Đó là "giải pháp" đau đớn cho tôi, sau khi đã bỏ ra hơn 20 giờ rất bực bội, cố gắng xây dựng một cái gì đó giống như những gì smileyborg đã đề xuất và thất bại trong nhiều tháng và ba phiên bản phát hành App Store.

Tôi xin lưu ý rằng nếu bạn thực sự cần hỗ trợ iOS 7 (đối với chúng tôi, điều đó rất cần thiết) thì công nghệ này quá dễ vỡ và bạn sẽ thử kéo tóc ra. Và UITableView hoàn toàn quá mức cần thiết trừ khi bạn đang sử dụng một số tính năng chỉnh sửa hàng nâng cao và / hoặc thực sự cần hỗ trợ hơn 1000 "hàng" (trong ứng dụng của chúng tôi, thực tế không bao giờ quá 20 hàng).

Phần thưởng được thêm vào là mã trở nên cực kỳ đơn giản so với tất cả các crap đại biểu và qua lại đi kèm với UITableView. Đây chỉ là một vòng mã duy nhất trong viewOnLoad trông thanh lịch và dễ quản lý.

Dưới đây là một số mẹo về cách thực hiện:

  1. Sử dụng Storyboard hoặc tệp nib, tạo ViewContoder và chế độ xem gốc được liên kết.

  2. Kéo qua UIScrollView vào chế độ xem gốc của bạn.

  3. Thêm các ràng buộc trên, dưới, trái và phải vào chế độ xem cấp cao nhất để UIScrollView lấp đầy toàn bộ chế độ xem gốc.

  4. Thêm một UIView bên trong UIScrollView và gọi nó là "container". Thêm các ràng buộc trên, dưới, trái và phải vào UIScrollView (mẹ của nó). KEY TRICK: Đồng thời thêm các ràng buộc "Độ rộng bằng nhau" để liên kết UIScrollView và UIView.

    LƯU Ý: Bạn sẽ gặp lỗi "chế độ xem cuộn có chiều cao nội dung có thể cuộn mơ hồ" và UIView chứa của bạn phải có chiều cao 0 pixel. Không có lỗi dường như là vấn đề khi ứng dụng đang chạy.

  5. Tạo tập tin nib và bộ điều khiển cho mỗi "ô" của bạn. Sử dụng UIView không phải UITableViewCell.

  6. Trong ViewContoder gốc của bạn, về cơ bản, bạn thêm tất cả các "hàng" vào vùng chứa UIView và thêm các ràng buộc theo chương trình liên kết các cạnh trái và phải của chúng với chế độ xem container, các cạnh trên của chúng vào đỉnh của khung nhìn container (cho mục đầu tiên) hoặc trước đó tế bào. Sau đó liên kết ô cuối cùng với đáy container.

Đối với chúng tôi, mỗi "hàng" nằm trong một tệp nib. Vì vậy, mã trông giống như thế này:

class YourRootViewController {

    @IBOutlet var container: UIView! //container mentioned in step 4

    override func viewDidLoad() {

        super.viewDidLoad()

        var lastView: UIView?
        for data in yourDataSource {

            var cell = YourCellController(nibName: "YourCellNibName", bundle: nil)
            UITools.addViewToTop(container, child: cell.view, sibling: lastView)
            lastView = cell.view
            //Insert code here to populate your cell
        }

        if(lastView != nil) {
            container.addConstraint(NSLayoutConstraint(
                item: lastView!,
                attribute: NSLayoutAttribute.Bottom,
                relatedBy: NSLayoutRelation.Equal,
                toItem: container,
                attribute: NSLayoutAttribute.Bottom,
                multiplier: 1,
                constant: 0))
        }

        ///Add a refresh control, if you want - it seems to work fine in our app:
        var refreshControl = UIRefreshControl()
        container.addSubview(refreshControl!)
    }
}

Và đây là mã cho UITools.addViewToTop:

class UITools {
    ///Add child to container, full width of the container and directly under sibling (or container if sibling nil):
    class func addViewToTop(container: UIView, child: UIView, sibling: UIView? = nil)
    {
        child.setTranslatesAutoresizingMaskIntoConstraints(false)
        container.addSubview(child)

        //Set left and right constraints so fills full horz width:

        container.addConstraint(NSLayoutConstraint(
            item: child,
            attribute: NSLayoutAttribute.Leading,
            relatedBy: NSLayoutRelation.Equal,
            toItem: container,
            attribute: NSLayoutAttribute.Left,
            multiplier: 1,
            constant: 0))

        container.addConstraint(NSLayoutConstraint(
            item: child,
            attribute: NSLayoutAttribute.Trailing,
            relatedBy: NSLayoutRelation.Equal,
            toItem: container,
            attribute: NSLayoutAttribute.Right,
            multiplier: 1,
            constant: 0))

        //Set vertical position from last item (or for first, from the superview):
        container.addConstraint(NSLayoutConstraint(
            item: child,
            attribute: NSLayoutAttribute.Top,
            relatedBy: NSLayoutRelation.Equal,
            toItem: sibling == nil ? container : sibling,
            attribute: sibling == nil ? NSLayoutAttribute.Top : NSLayoutAttribute.Bottom,
            multiplier: 1,
            constant: 0))
    }
}

"Gotcha" duy nhất tôi đã tìm thấy với cách tiếp cận này cho đến nay là UITableView có một tính năng hay của tiêu đề phần "nổi" ở đầu chế độ xem khi bạn cuộn. Giải pháp trên sẽ không làm điều đó trừ khi bạn thêm lập trình nhưng trong trường hợp cụ thể của chúng tôi, tính năng này không cần thiết 100% và không ai chú ý khi nó biến mất.

Nếu bạn muốn phân chia giữa các ô của mình, chỉ cần thêm UIView cao 1 pixel ở cuối "ô" tùy chỉnh trông giống như một dải phân cách.

Hãy chắc chắn bật "nảy" và "nảy theo chiều dọc" để điều khiển làm mới hoạt động và do đó, nó có vẻ giống như một chế độ xem bảng.

TableView hiển thị một số hàng và ngăn trống bên dưới nội dung của bạn, nếu nó không lấp đầy toàn màn hình trong khi giải pháp này không có. Nhưng về mặt cá nhân, tôi thích hơn nếu những hàng trống đó không có ở đó - với chiều cao ô thay đổi, nó luôn có vẻ "lỗi" đối với tôi để có các hàng trống trong đó.

Đây là hy vọng một số lập trình viên khác đọc bài viết của tôi TRƯỚC KHI lãng phí hơn 20 giờ để cố gắng tìm ra nó với Chế độ xem bảng trong ứng dụng của riêng họ. :)


11

Tôi đã phải sử dụng các chế độ xem động (chế độ xem thiết lập và các ràng buộc theo mã) và khi tôi muốn đặt chiều rộng của nhãn ưa thíchMaxLayoutWidth là 0. Vì vậy, tôi đã nhầm chiều cao của ô.

Sau đó tôi thêm vào

[cell layoutSubviews];

trước khi thực hiện

[cell setNeedsUpdateConstraints];
[cell updateConstraintsIfNeeded];

Sau đó, chiều rộng của nhãn là như mong đợi và chiều cao động được tính toán đúng.


9

Giả sử bạn có một ô có một khung nhìn phụ và bạn muốn chiều cao của ô đủ cao để bao quanh khung nhìn + đệm.

1) Đặt ràng buộc dưới cùng của khung nhìn phụ bằng với cell.contentView trừ đi phần đệm bạn muốn. Không đặt các ràng buộc trên chính ô hoặc cell.contentView.

2) Đặt thuộc tính của bảngView rowHeighthoặc tableView:heightForRowAtIndexPath:thànhUITableViewAutomaticDimension .

3) Đặt thuộc tính của bảngView estimatedRowHeighthoặctableView:estimatedHeightForRowAtIndexPath: dự đoán tốt nhất về chiều cao.

Đó là nó.


7

Nếu bạn bố trí theo chương trình, đây là những điều cần xem xét cho iOS 10 bằng cách sử dụng các neo trong Swift.

Có ba quy tắc / bước

SỐ 1: đặt hai thuộc tính này của chế độ xem bảng trên viewDidLoad, thuộc tính thứ nhất đang nói với chế độ xem bảng sẽ mong đợi kích thước động trên các ô của chúng, cách thứ hai chỉ là để ứng dụng tính kích thước của chỉ báo trên thanh cuộn, vì vậy nó giúp hiệu suất.

    tableView.rowHeight = UITableViewAutomaticDimension
    tableView.estimatedRowHeight = 100

SỐ 2: Điều quan trọng là bạn cần thêm các cuộc phỏng vấn vào nội dungView của ô không vào chế độ xem và cũng sử dụng bố cục của nó để neo các cuộc phỏng vấn lên trên cùng và dưới cùng, đây là một ví dụ hoạt động về cách thực hiện.

override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
    super.init(style: style, reuseIdentifier: reuseIdentifier)
    setUpViews()
}

private func setUpViews() {

    contentView.addSubview(movieImageView)
    contentView.addSubview(descriptionLabel)
    let marginGuide = contentView.layoutMarginsGuide

    NSLayoutConstraint.activate([
        movieImageView.heightAnchor.constraint(equalToConstant: 80),
        movieImageView.widthAnchor.constraint(equalToConstant: 80),
        movieImageView.leftAnchor.constraint(equalTo: marginGuide.leftAnchor),
        movieImageView.topAnchor.constraint(equalTo: marginGuide.topAnchor, constant: 20),

        descriptionLabel.leftAnchor.constraint(equalTo: movieImageView.rightAnchor, constant: 15),
        descriptionLabel.rightAnchor.constraint(equalTo: marginGuide.rightAnchor),
        descriptionLabel.bottomAnchor.constraint(equalTo: marginGuide.bottomAnchor, constant: -15),
        descriptionLabel.topAnchor.constraint(equalTo: movieImageView.topAnchor)

        ])
}

Tạo một phương thức sẽ thêm các mục con và thực hiện bố cục, gọi nó trong phương thức init.

SỐ 3: KHÔNG GỌI PHƯƠNG PHÁP:

  override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
    }

Nếu bạn làm điều đó, bạn sẽ ghi đè lên việc thực hiện của bạn.

Thực hiện theo 3 quy tắc này cho các ô động trong bảng xem.

đây là một triển khai làm việc https://github.com/jamesrochabrun/MinimalViewCont kiểm


trong Swift 5 UITableViewAutomaticDimension được đổi tên thành UITableView.automaticDimension
James Rochabrun

4

Nếu bạn có một chuỗi dài . ví dụ: cái không có ngắt dòng. Sau đó, bạn có thể gặp phải một số vấn đề.

Sửa chữa "bị cáo buộc" được đề cập bởi câu trả lời được chấp nhận và một vài câu trả lời khác. Bạn chỉ cần thêm

cell.myCellLabel.preferredMaxLayoutWidth = tableView.bounds.width

Tôi thấy câu trả lời của Suragh đầy đủ và súc tích nhất , do đó không khó hiểu.

Mặc dù không giải thích tại sao những thay đổi này là cần thiết. Hãy làm điều đó.

Thả mã sau vào một dự án.

import UIKit

class ViewController: UIViewController {

    lazy var label : UILabel = {
        let lbl = UILabel()
        lbl.translatesAutoresizingMaskIntoConstraints = false
        lbl.backgroundColor = .red
        lbl.textColor = .black
        return lbl
    }()

    override func viewDidLoad() {
        super.viewDidLoad()
        // step0: (0.0, 0.0)
        print("empty Text intrinsicContentSize: \(label.intrinsicContentSize)")
        // ----------
        // step1: (29.0, 20.5)
        label.text = "hiiiii"
        print("hiiiii intrinsicContentSize: \(label.intrinsicContentSize)")
        // ----------
        // step2: (328.0, 20.5)
        label.text = "translatesAutoresizingMaskIntoConstraints"
        print("1 translate intrinsicContentSize: \(label.intrinsicContentSize)")
        // ----------
        // step3: (992.0, 20.5)
        label.text = "translatesAutoresizingMaskIntoConstraints translatesAutoresizingMaskIntoConstraints translatesAutoresizingMaskIntoConstraints"
        print("3 translate intrinsicContentSize: \(label.intrinsicContentSize)")
        // ----------
        // step4: (328.0, 20.5)
        label.text = "translatesAutoresizingMaskIntoConstraints\ntranslatesAutoresizingMaskIntoConstraints\ntranslatesAutoresizingMaskIntoConstraints"
        print("3 translate w/ line breaks (but the line breaks get ignored, because numberOfLines is defaulted to `1` and it will force it all to fit into one line! intrinsicContentSize: \(label.intrinsicContentSize)")
        // ----------
        // step5: (328.0, 61.0)
        label.numberOfLines = 0
        print("3 translate w/ line breaks and '0' numberOfLines intrinsicContentSize: \(label.intrinsicContentSize)")
        // ----------
        // step6: (98.5, 243.5)
        label.preferredMaxLayoutWidth = 100
        print("3 translate w/ line breaks | '0' numberOfLines | preferredMaxLayoutWidth: 100 intrinsicContentSize: \(label.intrinsicContentSize)")

        setupLayout()
    }
    func setupLayout(){
        view.addSubview(label)
        label.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
        label.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
    }
}

Lưu ý rằng tôi chưa thêm kích thước nào ràng buộc . Tôi chỉ thêm các ràng buộc về trung tâm, trung tâm. Nhưng nhãn vẫn sẽ có kích thước chính xác Tại sao?

Bởi vì contentSize.

Để xử lý tốt hơn, trước tiên hãy giữ bước 0, sau đó nhận xét các bước 1-6. Hãy setupLayout()ở lại. Quan sát hành vi.

Sau đó bỏ ghi chú bước 1 và quan sát.

Sau đó bỏ ghi chú bước 2 và quan sát.

Làm điều này cho đến khi bạn không hoàn thành tất cả 6 bước và quan sát hành vi của họ.

Điều gì có thể kết luận từ tất cả điều này? Những yếu tố có thể thay đổi contenSize?

  1. Độ dài văn bản: Nếu bạn có một văn bản dài hơn thì chiều rộng của intinsicContentSize của bạn sẽ tăng lên
  2. Ngắt dòng: Nếu bạn thêm \nthì chiều rộng của intinsicContentSize sẽ là chiều rộng tối đa của tất cả các dòng. Nếu một dòng có 25 ký tự, dòng khác có 2 ký tự và dòng khác có 21 ký tự thì chiều rộng của bạn sẽ được tính dựa trên 25 ký tự
  3. Số dòng cho phép: Bạn phải thiết lập numberOfLinesđể 0nếu không bạn sẽ không có nhiều dòng. Bạn numberOfLinessẽ điều chỉnh chiều cao của IntinsicContentSize
  4. Thực hiện điều chỉnh: Hãy tưởng tượng rằng dựa trên văn bản của bạn, chiều rộng 200và chiều cao của IntenticContentSize của bạn là 100, nhưng bạn muốn giới hạn chiều rộng cho thùng chứa của nhãn, bạn sẽ làm gì? Giải pháp là đặt nó theo chiều rộng mong muốn. Bạn làm điều đó bằng cách thiết lập preferredMaxLayoutWidthđể 130sau đó intinsicContentSize mới của bạn sẽ có chiều rộng khoảng 130. Chiều cao rõ ràng sẽ nhiều hơn 100bởi vì bạn cần nhiều đường hơn. Điều đó đang được nói nếu các ràng buộc của bạn được đặt chính xác thì bạn sẽ không cần phải sử dụng điều này! Để biết thêm về điều đó xem câu trả lời này và ý kiến ​​của nó. Bạn chỉ cần sử dụng preferredMaxLayoutWidthnếu bạn không có các ràng buộc hạn chế chiều rộng / chiều cao vì trong một người có thể nói "không bao bọc văn bản trừ khi nó vượt quápreferredMaxLayoutWidth ". Nhưng với 100% chắc chắn nếu bạn đặt hàng đầu / đuôi và numberOfLinesđể 0sau đó bạn tốt!Câu chuyện dài hầu hết các câu trả lời ở đây khuyên bạn nên sử dụng nó là SAI! Bạn không cần nó. Cần nó là một dấu hiệu cho thấy các ràng buộc của bạn không được đặt chính xác hoặc bạn chỉ không có các ràng buộc

  5. Cỡ chữ: Cũng lưu ý rằng nếu bạn tăng kích thước phông chữ thì chiều cao của intinsicContentSize sẽ tăng. Tôi đã không thể hiện điều đó trong mã của mình. Bạn có thể tự mình thử nó.

Vì vậy, trở lại ví dụ bảngViewCell của bạn:

Tất cả bạn cần làm là:

  • thiết lập numberOfLinesđể0
  • hạn chế nhãn chính xác đến lề / cạnh
  • Không cần phải thiết lập preferredMaxLayoutWidth.

1

Trong trường hợp của tôi, tôi phải tạo một ô tùy chỉnh với một hình ảnh đến từ máy chủ và có thể có chiều rộng và chiều cao bất kỳ. Và hai UILabels với kích thước động (cả chiều rộng và chiều cao)

tôi đã đạt được điều tương tự ở đây trong câu trả lời của mình với tính năng tự động trả lời và lập trình:

Về cơ bản, câu trả lời @smileyBorg đã giúp nhưng systemLayoutSizeFitsSize không bao giờ có tác dụng với tôi, theo cách tiếp cận của tôi:

1. Không sử dụng thuộc tính tính chiều cao hàng tự động. 2.Không sử dụng chiều cao ước tính 3. Không cần cập nhật không cần thiết. 4.Không sử dụng Chiều rộng bố trí tối đa tự động ưa thích. 5. Không sử dụng systemLayoutSizeFitsSize (nên sử dụng nhưng không hoạt động với tôi, tôi không biết nó đang làm gì trong nội bộ), nhưng thay vào đó, phương thức của tôi - (float) getViewHeight hoạt động và tôi biết nó đang làm gì trong nội bộ.

Có thể có độ cao khác nhau trong Ô UITableView khi tôi sử dụng một số cách hiển thị ô khác nhau không?


1

Trong trường hợp của tôi, phần đệm là do độ cao của phầnHead và phầnFooter, trong đó bảng phân cảnh cho phép tôi thay đổi nó thành tối thiểu 1. Vì vậy, trong phương thức viewDidLoad:

tableView.sectionHeaderHeight = 0
tableView.sectionFooterHeight = 0

1

Tôi chỉ làm một số thử câm và lỗi với 2 giá trị của rowHeightestimatedRowHeightvà chỉ nghĩ rằng nó có thể cung cấp một số cái nhìn sâu sắc gỡ lỗi:

Nếu bạn đặt cả hai HOẶC chỉ đặt, estimatedRowHeightbạn sẽ có hành vi mong muốn:

tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 1.00001 // MUST be greater than 1

Chúng tôi khuyên bạn nên cố gắng hết sức để có được ước tính chính xác, nhưng kết quả cuối cùng không khác. Nó sẽ chỉ ảnh hưởng đến hiệu suất của bạn.

nhập mô tả hình ảnh ở đây


Nếu bạn chỉ đặt hàngHeight tức là chỉ làm:

tableView.rowHeight = UITableViewAutomaticDimension

kết quả cuối cùng của bạn sẽ không được như mong muốn:

nhập mô tả hình ảnh ở đây


Nếu bạn đặt thành estimatedRowHeight1 hoặc nhỏ hơn thì bạn sẽ gặp sự cố bất kể rowHeight.

tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 1 

Tôi gặp sự cố với thông báo lỗi sau:

Terminating app due to uncaught exception
'NSInternalInconsistencyException', reason: 'table view row height
must not be negative - provided height for index path (<NSIndexPath:
0xc000000000000016> {length = 2, path = 0 - 0}) is -1.000000'
    ...some other lines...

libc++abi.dylib: terminating with uncaught exception of type
NSException

1

Liên quan đến câu trả lời được chấp nhận bởi @smileyborg, tôi đã tìm thấy

[cell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize]

không đáng tin cậy trong một số trường hợp mà các ràng buộc không rõ ràng. Tốt hơn là buộc công cụ bố trí tính chiều cao theo một hướng, bằng cách sử dụng danh mục trình trợ giúp trên UIView bên dưới:

-(CGFloat)systemLayoutHeightForWidth:(CGFloat)w{
    [self setNeedsLayout];
    [self layoutIfNeeded];
    CGSize size = [self systemLayoutSizeFittingSize:CGSizeMake(w, 1) withHorizontalFittingPriority:UILayoutPriorityRequired verticalFittingPriority:UILayoutPriorityFittingSizeLevel];
    CGFloat h = size.height;
    return h;
}

Trong đó w: là chiều rộng của bảng xem


0

Chỉ cần thêm hai hàm này trong trình điều khiển khung nhìn của bạn, nó sẽ giải quyết vấn đề của bạn. Ở đây, danh sách là một chuỗi chuỗi chứa chuỗi của bạn mỗi hàng.

 func tableView(_ tableView: UITableView, 
   estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
        tableView.rowHeight = self.calculateHeight(inString: list[indexPath.row])

    return (tableView.rowHeight) 
}

func calculateHeight(inString:String) -> CGFloat
{
    let messageString = input.text
    let attributes : [NSAttributedStringKey : Any] = [NSAttributedStringKey(rawValue: NSAttributedStringKey.font.rawValue) : UIFont.systemFont(ofSize: 15.0)]

    let attributedString : NSAttributedString = NSAttributedString(string: messageString!, attributes: attributes)

    let rect : CGRect = attributedString.boundingRect(with: CGSize(width: 222.0, height: CGFloat.greatestFiniteMagnitude), options: .usesLineFragmentOrigin, context: nil)

    let requredSize:CGRect = rect
    return requredSize.height
}

-1
swift 4

    @IBOutlet weak var tableViewHeightConstraint: NSLayoutConstraint!
    @IBOutlet weak var tableView: UITableView!
    private var context = 1
 override func viewDidLoad() {
        super.viewDidLoad()

        self.tableView.addObserver(self, forKeyPath: "contentSize", options: [.new,.prior], context: &context)
    }
  // Added observer to adjust tableview height based on the content

    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
        if context == &self.context{
            if let size = change?[NSKeyValueChangeKey.newKey] as? CGSize{
                print("-----")
                print(size.height)
                tableViewHeightConstraint.constant = size.height + 50
            }
        }
    }

//Remove observer
 deinit {

        NotificationCenter.default.removeObserver(self)

    }

-1

Nếu chiều cao của ô là động bởi nội dung, bạn nên đếm chính xác và sau đó trả về giá trị chiều cao trước khi ô được hiển thị. Một cách dễ dàng là xác định phương thức đếm trong mã ô xem bảng để bộ điều khiển gọi ở phương thức ủy nhiệm chiều cao ô của bảng. Đừng quên tính chiều rộng khung ô thực (mặc định là 320) nếu chiều cao phụ thuộc vào chiều rộng của bảng hoặc màn hình. Nghĩa là, trong phương thức ủy nhiệm chiều cao của ô, sử dụng cell.frame để sửa chiều rộng ô trước, sau đó gọi phương thức đếm chiều cao được xác định trong ô để lấy giá trị phù hợp và trả về .

Tái bút Mã để tạo đối tượng ô có thể được định nghĩa trong một phương thức khác cho phương thức ủy nhiệm ô xem bảng khác nhau để gọi.


-3

UITableView.automaticDimension có thể được đặt qua Trình tạo giao diện:

Xcode> Storyboard> Inspector Size

Ô xem bảng> Chiều cao hàng> Tự động

Thanh tra kích thước


-4

một giải pháp iOs7 + iOs8 khác trong Swift

var cell2height:CGFloat=44

override func viewDidLoad() {
    super.viewDidLoad()
    theTable.rowHeight = UITableViewAutomaticDimension
    theTable.estimatedRowHeight = 44.0;
}

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    let cell =  tableView.dequeueReusableCellWithIdentifier("myTableViewCell", forIndexPath: indexPath) as! myTableViewCell
    cell2height=cell.contentView.height
    return cell
}

func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
    if #available(iOS 8.0, *) {
        return UITableViewAutomaticDimension
    } else {
        return cell2height
    }
}

lưu ý: systemLayoutSizeFitsSize không hoạt động trong trường hợp của tôi
djdance 16/1/2016

chiều cao ô không chính xác cellForRowAtIndexPath, ô chưa được đặt ra tại thời điểm này.
Andrii Chernenko

trong iOs7, nó là giá trị cố định, tức là nó hoạt động. Bạn có thể đặt bên ngoài cellForRowAtIndexPath nếu bạn muốn
djdance
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.