Sử dụng Bố cục Tự động trong UITableView cho bố cục ô động và chiều cao hàng thay đổi

1528
smileyborg 2013-09-12 06:48.

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

24 answers

2415
smileyborg 2013-09-12 06:48.

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

  • Dự án mẫu iOS 8 - Yêu cầu iOS 8
  • Dự án mẫu iOS 7 - Hoạt động trên iOS 7+

Mô tả khái niệm

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

1. Thiết lập & Thêm Ràng buộc

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

Hãy nhớ rằng, ý tưởng là để các chế độ xem phụ 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ể "tạo áp lực" và làm cho chế độ xem nội dung mở rộng cho vừa với chúng. Sử dụng một ô ví dụ với một vài lần xem phụ, đây là minh họa trực quan về một số (không phải tất cả!) Ràng buộc của bạn sẽ cần trông như thế nào:

Bạn có thể tưởng tượng rằng khi nhiều văn bản được thêm vào nhãn nội dung nhiều dòng trong ô ví dụ ở trên, nó sẽ cần phải phát triển theo chiều dọc để vừa với văn bản, điều này sẽ buộc ô phải phát triển theo 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 phù hợp để điều này hoạt động chính xác!)

Làm đúng các ràng buộc của bạn chắc chắn là phần khó nhất và quan trọng nhất để có được chiều cao ô động hoạt động 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ã 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à dễ dàng gỡ lỗi hơn rất nhiều khi có sự cố. Việc thêm các ràng buộc trong mã có thể dễ dàng và mạnh mẽ hơn đáng kể so với Trình tạo giao diện bằng cách sử dụng neo bố cục hoặc một trong những 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ừ bên 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, vì vậy để 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ã thêm ràng buộc của bạn bên updateConstraintstrong kiểm tra thuộc tính boolean chẳng hạn như didSetupConstraints(bạn đặt thành CÓ sau khi chạy ràng buộc -mã đệ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 điều 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 ô trong chế độ xem bảng duy nhất

Đối với mọi tập hợp ràng buộc duy nhất trong ô, hãy sử dụng số nhận dạng 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, thì mỗi bố cục duy nhất sẽ nhận được số nhận dạng tái sử dụng của chính nó. (Một gợi ý hay rằng bạn cần sử dụng số nhận dạng tái sử dụng mới là khi biến thể ô của bạn có số lần xem phụ khác hoặc các lần xem phụ được sắp xếp theo một kiểu riêng biệt.)

Ví dụ: nếu bạn đang hiển thị thư email trong mỗi ô, bạn có thể có 4 bố cục duy nhất: thư chỉ có chủ đề, thư có chủ đề và nội dung, thư có chủ đề và tệp đính kèm ảnh và thư có chủ đề, nội dung và tệp đính kèm ảnh. Mỗi bố cục có các ràng buộc hoàn toàn khác nhau được yêu cầu để đạt được nó, vì vậy khi ô được khởi tạo và các ràng buộc được thêm vào cho một trong các loại ô này, ô sẽ nhận được một số nhận dạng tái sử dụng duy nhất cụ thể cho loại ô đó. Điều này có nghĩa là khi bạn dequeue một ô để sử dụng lại, các ràng buộc đã được thêm vào và sẵn sàng sử dụ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ó chiều cao khác nhau! Đừng nhầm lẫn về cơ bản 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 kích thước nội dung khác nhau.

  • Không thêm các ô có các tập hợp ràng buộc hoàn toàn khác nhau vào cùng một nhóm tái sử dụng (tức 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 dequeue. 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 đề về hiệu suất lớn.

Đối với iOS 8 - Ô tự định kích thước

3. Bật ước tính chiều cao hàng

Để bật các ô chế độ xem bảng tự định 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 chỉ định một giá trị cho thuộc tính Ước tính RowHeight. 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 toán chiều cao thực tế của hàng

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

Với iOS 8, Apple đã nội bộ hóa phần lớn công việc mà trước đây bạn phải thực hiện trước iOS 8. Để cho phép cơ chế ô tự định cỡ 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 bậ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 làm là cung cấp cho chế độ xem bảng một ước tính tạm thời / trình giữ chỗ cho chiều cao hàng của các ô chưa có 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 thực tế của hàng sẽ được tính toán. Để xác định chiều cao thực tế cho mỗi hàng, chế độ xem bảng tự động hỏi mỗi ô 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 chế độ xem phụ 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 sẽ đượ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 / contentOffset 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 phải chính xác lắm - nó chỉ được sử dụng để định kích thướ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 công việc điều chỉnh chỉ báo cuộn cho các ước tính không chính xác khi bạn cuộn các ô trên màn hình. Bạn nên đặt thuộc estimatedRowHeighttính trên dạng xem bảng (trong viewDidLoadhoặc tương tự) thành một 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ó sự thay đổi lớn (ví dụ: khác nhau theo thứ tự độ lớn) và bạn nhận thấy chỉ báo cuộn "nhảy" khi cuộn thì bạn mới 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 định kích thước ô tự động)

3. Thực hiện vượt qua bố cục và lấy chiều cao ô

Đầu tiên, khởi tạo một phiên bản ngoài màn hình của ô xem bảng, một phiên bả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. (Ngoài màn hình có nghĩa là tham chiếu ô được lưu trữ trong thuộc tính / ivar trên bộ điều khiển chế độ xem và không bao giờ được trả về 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 định cấu hình với nội dung chính xác (ví dụ: văn bản, hình ảnh, v.v.) 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để lấy kích thước nhỏ nhất cần thiết để phù hợp với tất cả nội dung của ô. Sau đó, chiều cao có thể được trả về từ tableView:heightForRowAtIndexPath:phương thức ủy nhiệm.

4. Sử dụng Chiều cao hàng ước tính

Nếu chế độ xem bảng của bạn có hơn vài chục hàng trong đó, bạn sẽ thấy rằng việc giải quyết ràng buộc Bố cục Tự động có thể nhanh chóng làm hỏng chuỗi chính khi tải chế độ xem bảng lần đầu tiên, như tableView:heightForRowAtIndexPath:được gọi trên mỗi và mọi hàng khi tải đầu tiên ( để tính toán 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 làm là cung cấp cho chế độ xem bảng một ước tính tạm thời / trình giữ chỗ cho chiều cao hàng của các ô chưa có 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 thực tế của hàng 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 phải chính xác lắm - nó chỉ được sử dụng để định kích thướ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 công việc điều chỉnh chỉ báo cuộn cho các ước tính không chính xác khi bạn cuộn các ô trên màn hình. Bạn nên đặt thuộc estimatedRowHeighttính trên dạng xem bảng (trong viewDidLoadhoặc tương tự) thành một 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ó sự thay đổi lớn (ví dụ: khác nhau theo thứ tự độ lớn) và bạn nhận thấy chỉ báo cuộn "nhảy" khi cuộn thì bạn mới 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ả những điều 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 trong tableView:heightForRowAtIndexPath:, rất tiếc bạn sẽ cần phải triển khai một số bộ nhớ đệm cho chiều cao ô. (Đây là cách tiếp cận do các kỹ sư của Apple đề xuất.) Ý tưởng chung là để cho công cụ Autolayout giải quyết các hạn chế lần đầu tiên, sau đó lưu vào bộ nhớ cache chiều cao được tính toán cho ô đó và sử dụng giá trị được lưu trong bộ nhớ cache cho tất cả các yêu cầu trong tương lai cho chiều cao của ô đó. Tất nhiên, mẹo là đảm bảo bạn xóa chiều cao được lưu trong bộ nhớ cache cho một ô khi bất kỳ điều gì xảy ra có thể khiến chiều cao của ô thay đổi - chủ yếu, đây sẽ 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 Loại động).

Mã mẫu chung của iOS 7 (với nhiều nhận xét hấp dẫn)

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

  • Dự án mẫu iOS 8 - Yêu cầu iOS 8
  • Dự án mẫu iOS 7 - Hoạt động trên iOS 7+

Các dự án này là những ví dụ hoạt động đầy đủ về chế độ xem bảng với độ cao hàng thay đổi do ô chế độ xem bảng 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 do @KentBoogaart tổng hợp .

179
William Hu 2015-06-19 22:21.

Đố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, điều quan trọng là tính toán chiều cao sau khi tự động thanh toán:

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 .

97
Suragch 2016-04-05 23:23.

Ví dụ nhanh về chiều cao có thể thay đổi UITableViewCell

Đã cập nhật cho Swift 3

Câu trả lời Swift của William Hu rất hay, nhưng nó giúp tôi có một số bước đơn giản nhưng chi tiết khi học làm một việc 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 một UITableViewvới chiề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:

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

Nó có thể chỉ là một Ứng dụng Xem một lần.

Thêm mã

Thêm một tệp Swift mới vào dự án của bạn. Đặt tên nó là MyCustomCell. Lớp này sẽ giữ các cửa hàng cho các dạng xem 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ở ViewController.swift và đảm bảo rằng 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:

  • Hai dòng mã sau (cùng với bố cục tự động) làm cho chiều cao ô có thể thay đổi được:

    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 bộ đ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 cạnh. Sau đó kéo một Ô Dạng xem Bảng vào Dạng xem Bảng. Và vào ô Nguyên mẫu, kéo Nhãn. Sử dụng bố cục tự động để ghim nhãn vào bốn cạnh của dạng xem nội dung của Ô Dạng xem Bảng.

Lưu ý quan trọng:

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

Các cài đặt IB khác

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

Chọn Ô Table View và đặt lớp tùy chỉnh là 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 một chuỗi mà chúng tôi đã sử dụng cho cellReuseIdentifiermã trong mã ở trên.

Zero Lines cho nhãn

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

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

  • Kiểm soát kéo từ Chế độ xem bảng trong bảng phân cảnh đến tableViewbiến trong ViewControllermã.
  • Làm 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 mình ngay bây giờ và nhận được các ô có chiều cao thay đổi.

Ghi chú

  • Ví dụ này chỉ hoạt động cho iOS 8 trở lên. 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 duy nhất. Đảm bảo rằng bạn đã ghim đúng mọi thứ để bố cục 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ả năng chống nén dọc và ôm sát. 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 thiết lập nhãn để nhãn preferredMaxLayoutWidthbiết khi nào nên quấn dòng. Ví dụ: nếu bạn đã thêm ràng buộc Chiều ngang giữa vào 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

  • Tìm hiểu về ô tự định kích thước và loại động trong iOS 8
  • Ô Chế độ xem Bảng có Độ cao Hàng Thay đổi
  • Ví dụ về UITableView cho Swift
65
Adam Waite 2014-06-12 05:18.

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

Tôi quyết định đưa 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:quay lại 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 + AutoLayoutDynamicHeightCalculation.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 + AutoLayoutDynamicHeightCalculation.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ờ đã có!

57
Eddwin Paz 2015-02-11 09:41.

Đâ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ể hoạt động như mong đợi.

Objective-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
}
47
wildmonkey 2013-11-12 06:52.

Giải pháp do @smileyborg đề xuất gần như hoàn hảo. Nếu bạn có một ô tùy chỉnh và bạn muốn một hoặc nhiều ô UILabelcó độ cao động thì phương thức systemLayoutSizeFittingSize kết hợp với AutoLayout được bật sẽ trả về giá trị CGSizeZerotrừ khi bạn di chuyển tất cả các ràng buộc ô của mình từ ô sang contentView của nó (như được đề xuất bởi @TomSwift tại đây Cách thay đổi kích thước superview thành phù hợp với tất cả các lượt xem phụ với tính năng tự động thanh toán? ).

Để làm như vậy, bạn cần chè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];
    }
}

Kết hợp câu trả lời @smileyborg với câu trả lời này sẽ hoạt động.

25
Bob Spryn 2013-12-03 16:23.

Một câu chuyện đủ quan trọng mà 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 hầu hết là đúng. 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, chẳng hạn như cài đặt preferredMaxLayoutWidth, thì mã đó 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 đó là bởi vì chúng chỉ kích hoạt layoutSubviews trên contentView, không phải chính ô.

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 gọi setNeedsLayoutvì nó đã được đặt sẵn. Trong trường hợp bạn lưu một tham chiếu vào một ô, bạn có thể nên gọi nó. Dù bằng cách nào thì nó cũng không gây tổn hại gì.

Một mẹo khác nếu bạn đang sử dụng các lớp con của ô nơi bạn đang thiết lập những thứ như preferredMaxLayoutWidth. Như @smileyborg đề cập, "ô trong chế độ 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 đúng và sẽ gặp 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 của mình chứ không phải trong bộ điều khiển chế độ xem. Tuy nhiên, bạn có thể chỉ cần đặt khung ô tại thời điểm này bằng cách sử dụng chiều rộng bảng:

Ví dụ trong phép tính 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 vào bộ nhớ cache ô cụ thể này để sử dụng lại, nhưng điều đó không liên quan).

20
Alex Rouse 2014-03-06 05:53.

Trong trường hợp mọi người vẫn đang gặp khó khăn với điều này. Tôi đã viết một bài đăng blog nhanh về cách sử dụng Autolayout với UITableViews Tận dụng tính năng tự động thanh toán cho Dynamic Cell Heights cũng như một thành phần mã nguồn mở để giúp làm cho điều này trừu tượng hơn và dễ thực hiện hơn.https://github.com/Raizlabs/RZCellSizeManager

19
Chris Van Buskirk 2014-04-30 08:16.

Miễn là bố cục của bạn trong ô của bạn 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 tính năng thay đổi kích thước động được giới thiệu trong iOS 8.

19
Nikolay Suvandzhiev 2016-06-28 12:49.

(đối với Xcode 8.x / Xcode 9.x đọc ở dưới cùng)

Hãy cẩn thận với vấn đề sau trong Xcode 7.x, 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 định cỡ đú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 các đề xuất và lỗi khó hiểu cho bạn. Lý do là IB không muốn thay đổi chiều cao của hàng khi các ràng buộc của bạn ra lệnh (để ô phù hợp với nội dung của bạn). Thay vào đó, nó giữ cho chiều cao của hàng cố định và bắt đầu đề xuất bạn thay đổi các ràng buộc của mình 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, không có cảnh báo, không có lỗi, tất cả đều hoạt động.

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

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 Interface Builder 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.

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

Tôi đã thay đổi độ cao này từng lần một nhấp chuột (sử dụng bước lên / xuống) cho đến khi 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 đó, 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 không thực sự 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 - trong thời gian 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ý bảng điều khiển, bạn không gặp bất kỳ lỗi Tự động thanh toán 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ì giá trị phân số).

Để ngăn chặn các cảnh báo / lỗi IB khó chịu, bạn có thể chọn các chế độ xem có liên quan và trong Size Inspectorphần thuộc tính đã AmbiguitychọnVerify Position Only


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 ô để 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 Tự động thanh toán 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.

18
Krunal 2017-10-25 01:31.

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

  • Chỉ định và triển khai dữ liệu tableview
  • Gán UITableViewAutomaticDimensioncho rowHeight & ước tínhRowHeight
  • Triển khai các phương thức ủy quyền / dataSource (tức là heightForRowAtvà trả 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
}

Đối với trường hợp nhãn trong UITableviewCell

  • Đặt số dòng = 0 (& chế độ ngắt dòng = cắt ngắn đuôi)
  • Đặt tất cả các ràng buộc (trên, dưới, phải trái) đối với vùng chứa superview / cell 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 chiều dọc tối thiểu được bao phủ bởi nhãn, ngay cả khi không có dữ liệu.

Lưu ý : Nếu bạn có nhiều nhãn (UIElements) với độ dài động, thì độ dài này sẽ được điều chỉnh theo kích thước nội dung của nó: Điều chỉnh 'Mức độ ưu tiên ôm nội dung và kháng né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.

16
nickb 2014-07-08 15:34.

Giống như @ Bob-Spryn, tôi gặp phải 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 trong một thời gian. 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ó 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 phần tử bổ sung, hãy lấy ô của bạn với [tableView dequeueReusableCellWithIdentifier:@"DoseNeeded"](Không phải [tableView dequeueReusableCellWithIdentifier:forIndexPath:]vì điều này sẽ gây ra các vấn đề thú vị.) Khi bạn làm điều này, tất cả các phần tử bạn đã xác định trong IB sẽ được khởi tạo.

15
Mohnasm 2014-08-23 00:14.

Chiều cao ô trong chế độ xem bảng động và bố cục tự động

Một cách tốt để giải quyết vấn đề với Bố cục Tự động trong bảng phân cảnh:

- (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;
}
13
Abo3atef 2016-10-31 02:43.
tableView.estimatedRowHeight = 343.0
tableView.rowHeight = UITableViewAutomaticDimension

13
alpsystems.com 2015-06-15 12:17.

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

Đó là "giải pháp" đau đớn đối với tôi, sau khi phải bỏ ra hơn 20 giờ rất khó chịu theo đúng nghĩa đen để 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 của App Store.

Ý kiến ​​của tôi là nếu bạn thực sự cần hỗ trợ iOS 7 (đối với chúng tôi, đó là điều cần thiết) thì công nghệ này quá giòn và bạn sẽ phải cố gắng nhổ tóc. Và UITableView đó nói chung là 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ờ nhiều hơn 20 hàng).

Phần thưởng bổ sung là mã trở nên cực kỳ đơn giản so với tất cả những thứ tào lao dành cho đại biểu và qua lại đi kèm với UITableView. Nó chỉ là một vòng lặp 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 ViewController 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à "vùng chứa". Thêm các ràng buộc trên, dưới, trái và phải vào UIScrollView (cha của nó). KEY TRICK: Cũng thêm ràng buộc "Chiều 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 không rõ ràng" và UIView vùng chứa của bạn phải có chiều cao là 0 pixel. Dường như không có lỗi nào quan trọng khi ứng dụng đang chạy.

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

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

Đố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ư sau:

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))
    }
}

Điểm "gotcha" duy nhất mà 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 thú vị là 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 được điều đó trừ khi bạn thêm nhiều chương trình hơn nhưng đối với 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 nhận thấy khi nó biến mất.

Nếu bạn muốn có dải phân cách 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.

Đảm bảo bật "số trang bị trả lại" và "số lần trả lại theo chiều dọc" để điều khiển làm mới hoạt động và do đó, nó có vẻ giống chế độ xem bảng hơn.

TableView hiển thị một số hàng và dải phân cách 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 như giải pháp này không làm được. Nhưng theo cá nhân tôi, tôi thích nếu những hàng trống đó không có ở đó - với chiều cao ô thay đổi, nó luôn trông "lỗi" đối với tôi để có các hàng trống ở đó.

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

11
Mansurov Ruslan 2015-09-02 12:37.

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

Sau đó, tôi đã thêm

[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 đúng.

9
Vadoff 2016-07-19 07:32.

Giả sử bạn có một ô có chế độ xem phụ và bạn muốn chiều cao của ô đủ cao để bao gồm chế độ xem phụ + đệm.

1) Đặt giới hạn dưới cùng của subview bằng 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 tableView rowHeighthoặc tableView:heightForRowAtIndexPath:thành UITableViewAutomaticDimension.

3) Đặt thuộc tính của tableView estimatedRowHeighthoặc tableView:estimatedHeightForRowAtIndexPath:phỏng đoán tốt nhất về chiều cao.

Đó là nó.

7
James Rochabrun 2017-04-29 05:59.

Nếu bạn bố trí theo chương trình, đây là những gì cần xem xét đối với iOS 10 bằng cách sử dụng 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, chế độ xem đầu tiên cho biết chế độ xem bảng sẽ mong đợi kích thước động trên các ô của chúng, điều thứ hai là để ứng dụng tính toán kích thước của chỉ báo thanh cuộn, vì vậy nó sẽ giúp hiệu suất.

    tableView.rowHeight = UITableViewAutomaticDimension
    tableView.estimatedRowHeight = 100

SỐ 2: Điều này quan trọng là bạn cần thêm các lượt xem phụ vào contentView của ô không phải vào chế độ xem và cũng sử dụng layoutsmarginguide của nó để neo các lượt xem phụ ở trên cùng và dưới cùng, đây là một ví dụ hiệu quả 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 lượt xem phụ và thực hiện bố cục, gọi nó trong phương thức init.

SỐ 3: KHÔNG GỌI LÀ 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 đè việc triển khai của mình.

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

đây là một triển khai hoạt động https://github.com/jamesrochabrun/MinimalViewController

4
Honey 2018-10-06 11:10.

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

Bản sửa lỗi "bị cáo buộc" được đề cập bởi câu trả lời được chấp nhận và một số 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 là đầy đủ và ngắn gọn nhất , do đó không gây nhầm lẫn.

Mặc dù không giải thích lý do 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 bất kỳ ràng buộc kích thước nào . Tôi chỉ thêm các ràng buộc centerX, centerY. Nhưng nhãn sẽ được kích thước chính xác Tại sao?

Bởi vì contentSize.

Để xử lý điều này tốt hơn, trước tiên hãy giữ bước0, sau đó nhận xét ra 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 bỏ ghi chú tất cả 6 bước và quan sát hành vi của chúng.

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

  1. Độ dài văn bản: Nếu bạn có văn bản dài hơn thì chiều rộng của nội tạiContentSize sẽ tăng
  2. Ngắt dòng: Nếu bạn thêm \nthì chiều rộng của nội tạiContentSize 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. Của bạn numberOfLinessẽ điều chỉnh chiều cao nội tại của bạn
  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 của nội tạiContentSize là 200và chiều cao 100, nhưng bạn muốn giới hạn chiều rộng cho vùng chứa của nhãn, bạn sẽ làm gì? Giải pháp là đặt nó thành chiều rộng mong muốn. Bạn làm điều đó bằng cách thiết lập preferredMaxLayoutWidthđể 130sau đó intrinsicContentSize mới của bạn sẽ có chiều rộng xấp xỉ 130. Chiều cao rõ ràng sẽ nhiều hơn 100vì bạn cần nhiều dòng hơn. Điều đó đang được nói nếu các ràng buộc của bạn được thiết lập 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 đó, hãy xem câu trả lời này và nhận xét 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 như trong người ta có thể nói "không quấn 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 ngắn 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 đúng hoặc bạn không có các ràng buộc

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

Vì vậy, quay lại ví dụ tableViewCell của bạn:

Tất cả những gì bạn cần làm là:

  • thiết lập numberOfLinesđể0
  • ràng buộc nhãn một cách chính xác vào lề / cạnh
  • Không cần phải thiết lập preferredMaxLayoutWidth.
1
Alok 2016-05-27 20:12.

Trong trường hợp của tôi, tôi phải tạo một ô tùy chỉnh với 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 có 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 thanh toán và lập trình:

Về cơ bản, câu trả lời @smileyBorg ở trên đã giúp ích nhưng systemLayoutSizeFittingSize không bao giờ hiệu quả 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 toán độ 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ố cục Tối đa Ưu tiên Tự động. 5. Không sử dụng systemLayoutSizeFittingSize (lẽ ra phải sử dụng nhưng không hiệu quả với tôi, tôi không biết nó đang làm gì bên trong), mà thay vào đó phương pháp của tôi - (float) getViewHeight đang hoạt động và tôi biết nó đang làm gì trong nội bộ.

Có thể có các chiều 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
Rashid 2017-01-25 21:35.

Trong trường hợp của tôi, phần đệm là do chiều cao của sectionHeader và sectionFooter, nơi 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
Honey 2017-11-25 15:56.

Tôi vừa thực hiện một số thử và lỗi ngu ngốc với 2 giá trị của rowHeightestimatedRowHeightvà chỉ nghĩ rằng nó có thể cung cấp một số thông tin chi tiết về gỡ lỗi:

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

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

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.


Nếu bạn chỉ đặt rowHeight 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:


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
railwayparade 2017-12-19 12:31.

Đối với 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 khi 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 toán 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 chế độ xem bảng

0
Parth Barot 2018-05-16 20:08.

Chỉ cần thêm hai chức năng này vào bộ điều khiển chế độ xem của bạn, nó sẽ giải quyết được vấn đề của bạn. Ở đây, danh sách là một mảng chuỗi chứa chuỗi của bạn cho 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
}

Related questions

MORE COOL STUFF

Cate Blanchett chia tay chồng sau 3 ngày bên nhau và vẫn kết hôn với anh ấy 25 năm sau

Cate Blanchett chia tay chồng sau 3 ngày bên nhau và vẫn kết hôn với anh ấy 25 năm sau

Cate Blanchett đã bất chấp những lời khuyên hẹn hò điển hình khi cô gặp chồng mình.

Tại sao Michael Sheen là một diễn viên phi lợi nhuận

Tại sao Michael Sheen là một diễn viên phi lợi nhuận

Michael Sheen là một diễn viên phi lợi nhuận nhưng chính xác thì điều đó có nghĩa là gì?

Hallmark Star Colin Egglesfield Các món ăn gây xúc động mạnh đối với người hâm mộ tại RomaDrama Live! [Loại trừ]

Hallmark Star Colin Egglesfield Các món ăn gây xúc động mạnh đối với người hâm mộ tại RomaDrama Live! [Loại trừ]

Ngôi sao của Hallmark Colin Egglesfield chia sẻ về những cuộc gặp gỡ với người hâm mộ ly kỳ tại RomaDrama Live! cộng với chương trình INSPIRE của anh ấy tại đại hội.

Tại sao bạn không thể phát trực tuyến 'chương trình truyền hình phía Bắc'

Tại sao bạn không thể phát trực tuyến 'chương trình truyền hình phía Bắc'

Bạn sẽ phải phủi sạch đầu đĩa Blu-ray hoặc DVD để xem tại sao Northern Exposure trở thành một trong những chương trình nổi tiếng nhất của thập niên 90.

Where in the World Are You? Take our GeoGuesser Quiz

Where in the World Are You? Take our GeoGuesser Quiz

The world is a huge place, yet some GeoGuessr players know locations in mere seconds. Are you one of GeoGuessr's gifted elite? Take our quiz to find out!

8 công dụng tuyệt vời của Baking Soda và Giấm

8 công dụng tuyệt vời của Baking Soda và Giấm

Bạn biết đấy, hai sản phẩm này là nguồn điện để làm sạch, riêng chúng. Nhưng cùng với nhau, chúng có một loạt công dụng hoàn toàn khác.

Hạn hán, biến đổi khí hậu đe dọa tương lai của thủy điện Hoa Kỳ

Hạn hán, biến đổi khí hậu đe dọa tương lai của thủy điện Hoa Kỳ

Thủy điện rất cần thiết cho lưới điện của Hoa Kỳ, nhưng nó chỉ tạo ra năng lượng khi có nước di chuyển. Bao nhiêu nhà máy thủy điện có thể gặp nguy hiểm khi các hồ và sông cạn kiệt?

Quyên góp tóc của bạn để giúp giữ nước sạch của chúng tôi

Quyên góp tóc của bạn để giúp giữ nước sạch của chúng tôi

Tóc tỉa từ các tiệm và các khoản quyên góp cá nhân có thể được tái sử dụng như những tấm thảm thấm dầu và giúp bảo vệ môi trường.

Trong Saturday Night Live, The Bachelor is Bland và Tina Fey trở lại với vai 'Crazy' Sarah Palin

Trong Saturday Night Live, The Bachelor is Bland và Tina Fey trở lại với vai 'Crazy' Sarah Palin

Sau khi Sarah Palin tán thành Donald Trump vào đầu tuần này, gần như không thể tránh khỏi việc Tina Fey sẽ trở lại Saturday Night Live để thăm lại ấn tượng Palin cổ điển của cô. Và Fey chắc chắn đã không làm thất vọng, cô ấy đã đưa ra một lời khen ngợi không hề nhẹ về bài phát biểu chứng thực Iowa quanh co và khó hiểu của Palin trong khi Trump của Darrell Hammond đưa ra bình luận xuyên suốt.

Đây có phải là sự khởi đầu cho sự kết thúc của việc giam giữ Brittney Griner?

Đây có phải là sự khởi đầu cho sự kết thúc của việc giam giữ Brittney Griner?

Brittney Griner (r.) Ngay từ đầu, thân phận của Brittney Griner đã là tình huống con tin Mỹ độc nhất trong lịch sử hiện đại.

Tom Brady là bộ tứ vệ đầu tiên cuối cùng có thể giúp Julio Jones có hơn 10 lần chạm bóng trong một mùa giải

Tom Brady là bộ tứ vệ đầu tiên cuối cùng có thể giúp Julio Jones có hơn 10 lần chạm bóng trong một mùa giải

Chúng ta có thể thấy nhiều hơn nữa về một Julio Jones khỏe mạnh trong khu vực cuối năm nay. John Parker Wilson, Greg McElroy, A.

Đó phải là Đức

Đó phải là Đức

Đối với đội tuyển Anh, không có kẻ thủ ác nào lớn hơn Hầu hết các cổ động viên Anh, nếu không muốn nói là tất cả, hẳn sẽ phải gật gù khi tiếng còi mãn cuộc của trận bán kết lượt về W Euro 2022 vang lên. Bởi vì nó báo hiệu rằng Đức sẽ chờ đợi ở Wembley trong trận chung kết với Anh và là điều duy nhất giữa Anh và chiếc cúp lớn đầu tiên của đội tuyển nữ.

Nicky Hilton Forced to Borrow Paris' 'I Love Paris' Sweatshirt After 'Airline Loses All [My] Luggage'

Nicky Hilton Forced to Borrow Paris' 'I Love Paris' Sweatshirt After 'Airline Loses All [My] Luggage'

Nicky Hilton Rothschild's luggage got lost, but luckily she has an incredible closet to shop: Sister Paris Hilton's!

Kate Middleton dành một ngày bên bờ nước ở London, cùng với Jennifer Lopez, Julianne Hough và hơn thế nữa

Kate Middleton dành một ngày bên bờ nước ở London, cùng với Jennifer Lopez, Julianne Hough và hơn thế nữa

Kate Middleton dành một ngày bên bờ nước ở London, cùng với Jennifer Lopez, Julianne Hough và hơn thế nữa. Từ Hollywood đến New York và mọi nơi ở giữa, hãy xem các ngôi sao yêu thích của bạn đang làm gì!

17 tuổi bị đâm chết trong khi 4 người khác bị thương trong một cuộc tấn công bằng dao trên sông Wisconsin

17 tuổi bị đâm chết trong khi 4 người khác bị thương trong một cuộc tấn công bằng dao trên sông Wisconsin

Các nhà điều tra đang xem xét liệu nhóm và nghi phạm có biết nhau trước vụ tấn công hay không

Thanh thiếu niên, Gia đình Florida Hội đồng quản trị trường học về Luật 'Không nói đồng tính': 'Buộc chúng tôi tự kiểm duyệt'

Thanh thiếu niên, Gia đình Florida Hội đồng quản trị trường học về Luật 'Không nói đồng tính': 'Buộc chúng tôi tự kiểm duyệt'

Vụ kiện, nêu tên một số học khu, lập luận rằng dự luật "Không nói đồng tính" được ban hành gần đây của Florida "có hiệu quả im lặng và xóa bỏ học sinh và gia đình LGBTQ +"

Đường băng hạ cánh

Đường băng hạ cánh

Cuối hè đầu thu là mùa hoài niệm. Những chiếc đèn đường chiếu ánh sáng của chúng qua những con đường đẫm mưa, và những chiếc lá dưới chân - màu đỏ cam tắt trong bóng chạng vạng - là lời nhắc nhở về những ngày đã qua.

Hãy tưởng tượng tạo ra một chiến lược nội dung thực sự CHUYỂN ĐỔI. Nó có thể.

Hãy tưởng tượng tạo ra một chiến lược nội dung thực sự CHUYỂN ĐỔI. Nó có thể.

Vào năm 2021, tôi khuyến khích bạn suy nghĩ lại mọi thứ bạn biết về khách hàng mà bạn phục vụ và những câu chuyện bạn kể cho họ. Lùi lại.

Sự mất mát của voi ma mút đã mở ra trái tim tôi để yêu

Sự mất mát của voi ma mút đã mở ra trái tim tôi để yêu

Vào ngày sinh nhật thứ 9 của Felix The Cat, tôi nhớ về một trong những mất mát lớn nhất trong cuộc đời trưởng thành của tôi - Sophie của tôi vào năm 2013. Tôi đã viết bài luận này và chia sẻ nó trên nền tảng này một thời gian ngắn vào năm 2013.

Khi bạn không thể trở thành người mà Internet muốn bạn trở thành

Khi bạn không thể trở thành người mà Internet muốn bạn trở thành

Tôi ghét từ "tàu đắm". Mọi người cảm thấy thoải mái trong la bàn đạo đức của riêng mình, và khi làm như vậy, họ thấy mình vượt qua sự phán xét.

Language