Auto Layout With Self Resizing Cell

Ở phần trước (https://viblo.asia/quocnb/posts/7rVRqwo3G4bP), tôi đã giới thiệu sơ qua về Autolayout và cách thực hiện các một màn hình đơn giản, sử dụng autolayout. Bài viết chúng ta sẽ tìm hiểu về Constraint, cũng như ứng dụng nó để làm việc với UITableViewCell, thứ mà chúng ta đã, đang và sẽ làm việc rất rất nhiều với nó.

I. Tìm hiểu về Constraint

Các layout của view được biểu diễn qua các phương trình tuyến tính, và mỗi constraint chính là một phương trình. Và như đã nói ở phần trước, điều bạn cần làm, là thể hiện mỗi constraint qua một và chỉ một phương trình khả dụng nhất có thể.

Screen Shot 2015-11-27 at 9.51.41 PM.png

Trong 1 số trường hợp, như khi xét width, height, hoặc aspect ratio thì item2 để bằng nil, relationship = Not An Attribute, và multiper = 0

1. Attribute###

Trong Autolayout, mỗi attribute đại diện cho một phần của view được đưa ra để tính toán, để xác định layout của view thông qua nó. Các Attribute thường là 4 biên (trên, dưới, trái, phải), hoặc là width, height , center x, center y Bảng minh họa các Attribute của view Screen Shot 2015-11-27 at 10.30.05 PM.png

2.Relationship

Nếu mỗi attribute đại diện cho một phần của view thì mỗi relationship lại thể hiện mối quan hệ giữa attribute đó với các attribute khác. Các quan hệ giữa các attribute có thể là quan hệ bình đẳng, hoặc bất bình đẳng.

// A single equal relationship
Blue.leading = 1.0 * Red.trailing + 8.0

// Can be replaced with two inequality relationships
Blue.leading >= 1.0 * Red.trailing + 8.0
Blue.leading <= 1.0 * Red.trailing + 8.0

Điều này sẽ giúp ích rất nhiều trong việc xác định mối quan hệ tương quan giữa các view, nó không chỉ dùng để thể hiện được vị trí chính xác của view mà còn có thể sử dụng để giới hạn vị trí của các layout

3. Priorities

Phần này không hiển thị ở trong phương trình ví dụ trên, nhưng nó đóng vai trò quan trọng trong việc xác định độ ưu tiên khi các constraint bị xung khắc lẫn nhau. Mọi constraint đều có priority từ 1-1000. Giá trị mặc định của mỗi constraint là 1000 (bắt buộc), các giá trị còn lại là optional. Tuy có 4 giá trị priority chính là 250 (low), 500 (medium), 750 (high) và 1000 (required), nhưng với mỗi priority giữa các constraint có thể chỉ cần hơn kém nhau 1-2 giá trị để thể hiện sự ưu tiên giữa các constraint

II. Tạo App demo

Tiếp tục project ở phần 1, chúng ta tạo một Tableview để hiển thị Staff list. Cell để hiển thị thông tin của staff bao gồm các phần

  • Avatar -> Image View
  • Name -> Label
  • Position -> Label
  • Note -> Label

Ở phần này, chúng ta mong muốn là các thông tin sẽ được hiển thị đầy đủ, với các nội dung khác nhau. Hay nói cách khác, nội dung của cell là động, và kích thước của cell cũng phải update theo nội dung mà nó đang chứa. Tôi sẽ trình bày việc tạo một cell có thể update kích cỡ động theo từng bước, thông qua việc hiển thị các cell lỗi

1. Thực hiện autoresizing cell

Triển khai phần tạo cell và xét autolayout cho các phần như sau Screen Shot 2015-11-27 at 3.25.04 PM.png

Setup Autolayout

Screen Shot 2015-11-27 at 3.13.05 PM.png

**- Hiện tượng : **

Đối với những trường text quá dài, text sẽ bị cắt mất Simulator Screen Shot Nov 27, 2015, 3.34.22 PM.png

**- Nguyên nhân : **

Việc xét height của cell không chuẩn, làm cho các lable bị cắt mất phần hiển thị

**- Giải pháp : **

Để support việc tạo ra các self resizing cell như thế này, Apple đã cung cấp một method để tính toán tự động height của cell

cell.contentView.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize)

Chúng ta sẽ update cell height qua hàm

	override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
        if (self.sizingCell == nil) {
            self.sizingCell = tableView.dequeueReusableCellWithIdentifier("MainInfoTableViewCell")
                as? MainInfoTableViewCell
        }
        let info : NSDictionary = self.data[indexPath.row]
        self.setupCellWithDictionary(self.sizingCell!, info: info)
        self.sizingCell?.setNeedsLayout()
        self.sizingCell?.layoutIfNeeded()
        let bestCellHeight : CGFloat = (self.sizingCell?.contentView
            .systemLayoutSizeFittingSize(UILayoutFittingCompressedSize).height)!
        let separatorLineHeight : CGFloat = 1.0
        return bestCellHeight + separatorLineHeight
    }

**- Kết quả sau khi Fix **

Simulator Screen Shot Nov 27, 2015, 4.35.32 PM.png

2. Cell đã autoresizing nhưng vẫn chưa hiển thị hết text

Kết thúc phần 1, chúng ta đã tạo ra 1 cell có thể auto resize dựa vào nội dung mà nó đang hiển thị. Nhưng có vẻ như hàm tính height đã bị nhầm lẫn, các trường text đã hiển thị được nhiều text hơn nhưng vẫn chưa đủ

** Nguyên nhân : **

Do label.preferredMaxLayoutWidth đang xét không chuẩn, khiến việc tính height tự động bị sai lệch

** Khắc phục : **

Update label.preferredMaxLayoutWidth

override func layoutSubviews() {
        super.layoutSubviews()
        updatePreferredMaxLayoutWidth(self.nameLabel)
        updatePreferredMaxLayoutWidth(self.positionLabel)
        updatePreferredMaxLayoutWidth(self.noteLabel)
    }
    func updatePreferredMaxLayoutWidth(label : UILabel) {
        let rightPadding : CGFloat = 8.0;
        label.preferredMaxLayoutWidth = CGRectGetWidth(UIScreen.mainScreen().bounds) - CGRectGetMinX(label.frame) - rightPadding;
    }

** Kết quả sau khi fix **

Simulator Screen Shot Nov 27, 2015, 4.41.12 PM.png

3. Cell hiển thị chưa chuẩn khi có 2 hoặc nhiều view có vị trí tương quan tương đương nhau

Lần này, height của cell đã được tính rất chuẩn, nhưng với các cell có text ngắn, phần ảnh sẽ bị sát với lề dưới của cell

** Nguyên nhân : **

Do phần bottom của image view không được xét autolayout, và cần chú ý rằng nếu xét lại phần bottom constraint cho imageview như bình thường, nó sẽ bị xung đột với phần bottom của Note Label

** Giải pháp : **

  • Xét bottom constraint cho imageview, xét relation >=
  • xét lại bottom constraint relation cho Note Label thành >=

Screen Shot 2015-11-27 at 4.55.28 PM.png

** Kết quả sau khi fix **

Simulator Screen Shot Nov 27, 2015, 4.51.49 PM.png

Tổng Kết

Ở phần trên, chúng ta đã thực hiện được việc auto resizing cell dựa trên nội dung mà nó sẽ phải hiển thị. Bài viết này còn rất sơ sài nhưng tôi hi vọng có thể giúp các bạn giải quyết được vấn đề mà chúng ta thường gặp phải khi làm việc với các cell động. Hi vọng rằng với việc sử dụng kiến thức về các thành phần trong phương trình constraint, các bạn có thể thực hiện được các công việc liên quan tới layout một cách dễ dàng và thuần thục hơn.

Và như thường lệ, các bạn có thể tham khảo phần code ở đây https://github.com/quocnb/AutoLayoutResearch