12/08/2018, 12:39

Auto Layout in IOS

Đối với việc lập trình iOS một vài năm trước đây thì chúng ta có thể bỏ qua khái niệm về Auto layout. Tuy nhiên thì giờ đây thì apple đã cho ra nhiều thiết bị mới có kích thước màn hình khác nhau như iPhone 5, 6, 6+ ngoài ra còn có thêm các thiết bị có màn hình lớn hơn như iPad thì áp dụng Auto ...

Đối với việc lập trình iOS một vài năm trước đây thì chúng ta có thể bỏ qua khái niệm về Auto layout. Tuy nhiên thì giờ đây thì apple đã cho ra nhiều thiết bị mới có kích thước màn hình khác nhau như iPhone 5, 6, 6+ ngoài ra còn có thêm các thiết bị có màn hình lớn hơn như iPad thì áp dụng Auto layout vào ứng dụng để nó có thể chạy trên nhiều màn hình khác nhau là hoàn toàn hợp lý.

1. Auto Layout là gì?

Auto Layout là một hệ thống nó giúp cho việc hỗ trợ dễ dàng hơn trên nhiều loại màn hình có kích thước khác nhau với 1 giao diện bằng cách tạo ra các giao diện có thể thay đổi với các loại màn hình khác nhau. Nó chính là một tập hợp các ràng buộc đã được mô tả trong giao diện của chúng ta.

2. Tại sao nên chọn Auto Layout

Auto Layout làm cho việc lập trình đơn giản hơn trong việc viết chương trình hay bảo trì phần mềm và rút ngắn thời gian dành cho việc viết chương trình của bạn trong việc hỗ trợ nhiều màn hình khác nhau. Điều này có nghĩa là giúp rút ngắn thời gian code và sửa lỗi hơn. Nó cũng rất dễ để hiểu được các ràng buộc hơn là các phương pháp tiếp cận khác bởi ở đây là chúng ta sẽ dùng từ ngữ nhiều hơn thay vì dùng toán học.

3. Ràng buộc sắp xếp

các ràng buộc trong sắp xếp vị trí được miêu tả là các thuộc tính toán học và mối liên hệ giữa các view. Lớp NSLayoutConstraint được sử dụng để tạo ra các ràng buộc trên các iOS và macOS. Tất cả các ràng buộc đều có một hệ số và một hằng số. Có một số kiểu ràng buộc như sau:

  • Các ràng buộc kích thước: chiều rộng của một cái ảnh là 200
  • Các ràng buộc về căn chỉnh, sắp hàng: một label nên được đặt chính giữa của màn hình
  • Các ràng buộc về khoảng cách: khoảng cách giữa hai labels hoặc giữa một view và lề của màn hình

Auto Layout sẽ xác định khung cho mỗi view trên màn hình của bạn. Với mỗi view sẽ có một số ràng buộc sẽ giúp chúng ta xác định được chiều dài, chiều rộng, toạ độ x, y. Có một số ngoại lệ thì chúng ta sẽ tìm hiểu thêm sau.

Mối quan hệ với phần tử cha

Một view có thể thay đổi kích thước của nó dựa trên chính thành phần superview (thành phần chứa view đó hay thành phần cha). Một trong số rất các điều phổ biến chúng ta thường nghĩ đến đó là các view sẽ lấp đầy phần tử cha của nó. Tuy nhiên khi hiển thị ra màn hình nhìn cái đó khá hài hước. Khi làm việc với Xcode, thực hiện việc cho một phần tử view vào 1 phần tử cha và thay đổi kích thước của phần tử con cho đến khi lấp đầy phần tử cha thì sẽ xuất hiệnc các đường thẳng màu xanh nó thông báo cho chúng ta biết nó đã nhận được kích thước chính xác của phần tử con. Khi chúng ta gán cho nó một màn nền thì chúng ta sẽ biết được giao diện của view mà chúng ta vừa làm việc sẽ có kích thước thế nào. Trong Xcode cũng có một chức năng giúp chúng ta nhìn thấy được giao diện mà chúng ta vừa xây dựng mà không cần phải build chương trìnhd để chạy trên các thiết bị thật hay ảo đó chính là Assistant Editor. Sử dụng công cụ này chúng ta sẽ có thể nhìn thấy ngay phần tử con của chúng ta vừa làm việc sẽ được bố trí thế nào trên màn hình thiết bị của chúng ta. với kích thước phần tử cha giống với thích thước khi chúng ta làm việc với phần tử con thì ở giao diện chúng ta thấy là nó lấp đầy phần tử cha của nó hay với kích thước nhỏ hơn chúng ta cũng thấy được điều đó tuy nhiên với các màn hình có kích thước lớn hơn ta lại thấy thành phần cha của nó không bị lấp đầy bởi thành phần con. Bây giờ chúng ta sẽ cần tạo ra các ràng buộc để trên các màn hình dù lớn hơn hay nhỏ hơn thì thành phần con của chúng ta cũng sẽ lấp đầy thành phần cha. Có rất nhiều các để tạo ra một contraint nhưng ở đây tôi giới thiệu cách mà tôi thường sử dụng đó chính là giữ chuột phải vào thành phần con và kéo đến thành phần cha. Sau đó một hộp chọn sẽ hiện ra các kiểu ràng buộc để chúng ta có thể lựa chọn. Đối với trường hợp của chúng ta là để cho thành phần con lấp đầy thành phần cha thì chúng ta cần thêm vào 4 ràng buộc là cố định khoảng cách với 4 lề của thành phần cha.

  • Leading Space to Container (ràng buộc khoảng cách với lề trái)
  • Trailing Space to Contrainer (ràng buộc khoảng cách với lề phải)
  • Top Space to Container Margin (ràng buộc khoảng cách với lề trên)
  • Bottom Space to Container Margin (ràng buộc khoảng cách với lề dưới)

với 4 ràng buộc đơn giản này thì thành phần view con của chúng ta sẽ lấp đầy thành phần cha trên tất cả các màn hình của các thiết bị có hệ điều hành là iOS. Ở đây thì các giá trị của chúng ta là 0 tuy nhiên các nhà phát triển cũng có thể tuỳ ý thay đổi để sao cho phù hợp với thiết kế giao diện.

Tính không đồng nhất

các label thường không hiển thị được trọn vẹn trên các thiết bị có màn hình nhỏ. Điều này chính là do nó luôn giữ độ dài nó được khởi tạo trên các giao diện. Các ràng buộc không chỉ có là các giá trị bằng mà ngoài ra nó còn có các giá trị lớn hơn hoặc nhỏ hơn. Giá trị mặc định mà chúng ta tạo ra ở file giao diện thì nó mặc định là bằng nhau tuy nhiên chúng ta cũng có thể thay đổi được bằng cách sửa chữa các ràng buộc thay dấu bằng bằng dấu lớn hơn hoặc bằng hoặc dấu nhỏ hơn hoặc bằng.

Mối quan hệ với các đối tượng ngang hàng

các phần tử con cùng 1 phần tử cũng có mối quan hệ với nhau. Khi các phần tử của cùng 1 phần tử cha thì chúng ta có thể thêm các ràng buộc về khoảng cách giữa 2 phần tử hoặc thêm các ràng buộc về căn trên, dưới trái phải ở giữa của 2 phần tử.

aspect ratio

các view của chúng ta có thể hiển thị rất đẹp trên một màn hình mà chúng ta xây dựng. Tuy nhiên trên các màn hình khác nó dẫn tới méo hình như thế trông rất xấu. Như vậy chúng ta có thể thêm một ràng buộc về tỉ lệ của các kích thước. chúng ta có thể tạo các ràng buộc về tỉ lệ kích thước của nó với tỉ lệ màn hình hoặc cũng có thể tạo được các tỉ lệ kích thước của nó với chính kích thước khác của chúng.

ngăn xếp View

chúng ta có thể bố trí giao diện của chúng ta với nhiều view khác nhau tuy nhiên trên một số màn hình to hoặc bé thì cách này có thể gây ra hiện tượng các view không đặt ở các vị trí mong muốn. Đối với các tính năng mới của iOS9 thì chúng ta có một lớp hỗ trợ khá tốt cho việc bố cục các view theo mong muốn nhờ có UIStackView. UIStackView cũng sử dụng Auto Layout để bố trí các thành phần con mà nó chứa theo chiều dọc hoặc theo chiều ngang. Chúng ta cũng có thể chỉnh sửa lại được khoảng cách, căn lề và các thành phần khác của các phần tử con trong stackView.

Các kích thước bên trong

Khi chúng ta thay đổi kích thước font chữ của một label thì kích thước của label cũng thay đổi. Đối với việc này thì chúng ta có thể xử lý bằng cách override lại phương thức intrinsicContentSize(). Một số thành phần như UISwitch thì nó có các kích thước có sẵn cho cả hai chiều hoặc một số thành phần khác thì có kích thước chiều cao xác định sẵn còn lại kích thước chiều rộng có thể thay đổi được. Trong trường hợp mà nội dung bị thay đổi thì chúng ta có thể gọi phương thức trên để cập nhật lại màn hình của bạn.

Margins

Nhiều khi bạn muốn thêm các thành phần vào một bên của 1 phần tử cha, tuy nhiên lại không muốn nó bắt đầu từ đường bên lề của cha. Các kích thước này có thể phụ thuộc vào kích cỡ màn hình. Để làm điều này được dễ dàng thì chúng ta cần phải làm cho các ràng buộc liên quan tới các mafgin của iOS. Để thay đổi margin thì chúng ta có thể set như sau:

container.layoutMargins = UIEdgeInsets(top: 0, left: 20, bottom: 10, right: 20)

Contraints với code

Các contraints không chỉ đơn giản là làm việc trên các giao diện để kéo thả mà chúng ta cũng có thể thêm chúng vào bằng code tuy nhiên trong nhiều trường hợp thì không cần nhưng trường hợp trong dự án của tôi thì cần thiết. Tôi sẽ mô tả sau để các bạn hiểu hơn để khi thêm vào bằng code sẽ hiệu quả khi nào. Chúng ta có 3 cách để có thể tạo ra các ràng buộc từ code:

  • Tạo ra một NSLayoutContraint và cài đặt các thuộc tính cho nó
  • sử dụng một format để tạo ra nhiều ràng buộc trong 1 lần
  • sử dụng các thư viện NSLayoutContraint
let leadingConstraint =
    NSLayoutConstraint(item: self.redView,
        attribute: NSLayoutAttribute.Leading,
        relatedBy: NSLayoutRelation.Equal,
        toItem: self.view,
        attribute: NSLayoutAttribute.Leading,
        multiplier: 1,
        constant: 50)

let trailingConstraint =
    NSLayoutConstraint(item: self.redView,
        attribute: NSLayoutAttribute.Trailing,
        relatedBy: NSLayoutRelation.Equal,
        toItem: self.view,
        attribute: NSLayoutAttribute.Trailing,
        multiplier: 1,
        constant: -50)

let topConstraint =
    NSLayoutConstraint(item: self.redView,
        attribute: NSLayoutAttribute.Top,
        relatedBy: NSLayoutRelation.Equal,
        toItem: self.view,
        attribute: NSLayoutAttribute.Top,
        multiplier: 1,
        constant: 50)

let bottomConstraint =
    NSLayoutConstraint(item: self.redView,
        attribute: NSLayoutAttribute.Bottom,
        relatedBy: NSLayoutRelation.Equal,
        toItem: self.view,
        attribute: NSLayoutAttribute.Bottom,
        multiplier: 1,
        constant: -50)

let constraints = [
    leadingConstraint,
    trailingConstraint,
    topConstraint,
    bottomConstraint
]

self.view.addConstraints(constraints)

Ngoài ra còn có thể add các contraint bằng dạng visual Format như sau

let bindings = Dictionary(dictionaryLiteral: ("redView", self.redView))

let horizontalConstraints =
    NSLayoutConstraint.constraintsWithVisualFormat(
        "H:|-50-[redView]-50-|",
        options: nil,
        metrics: nil,
        views: bindings)
self.view.addConstraints(horizontalConstraints)

let verticalConstraints =
    NSLayoutConstraint.constraintsWithVisualFormat(
        "V:|-50-[redView]-50-|",
        options: nil,
        metrics: nil,
        views: bindings)
self.view.addConstraints(verticalConstraints)

Tổng kết

Như vậy thì Auto layout là một công cụ tốt và để mặc định cho giao diện của iOS. Mặc dù chúng ta biết hầu hết các trường hợp thì cũng có hướng giải quyết trên màn hình giao diện nhưng Auto layout cũng chính là một sự lựa chọn hoàn hảo.

0