Có thể các bạn đã biết progress view của Apple nó khá là đơn giản và ýt thuộc tính để chúng ta có thể sử dụng cũng như thay đổi để phù hợp với thiết kế. Trong dự án gần đây mình đã có cơ hội để động vào nó. Trong thời gian đầu mình tự mày mò thì progress bar không hỗ trợ việc như vậy. Vì vậy mình viết bài này để chia sẻ về cách mình đã custom lại progress này. Hi vọng nó giúp các bạn gặp khó khăn với việc custom lại progress bar.
Vấn đề cần giải quyết: Làm thế nào để bo tròn góc của progress bar
Đây là progress view mình cần đạt được:
Đây là progress bar mặc đinh của apple:
Phân tích
Khi thử đọc tài liệu về UIProgressView. Không có thuộc tính nào giúp mình có thể đạt được điều mình muốn.
Để bo tròn cả progressview thì ta có thể dùng layer.cornerRadius, nhưng để bo tròn thằng progress chạy bên trong thì sao? Chúng ta cần tìm được cái view đó và bo nó lại.
Giải quyết bài toán
Bước 1: Chúng ta cần tạo constraint height cho progressView
Chúng ta có 2 cách để tăng height cho ProgressView: 1. Sử dụng Transform Scale: Nó sẽ làm cornerRadius chạy không đúng 2. Sử dụng constraint height: Nên chúng ta chon cách này
Tuy rằng chúng ta đã constraint height của nó bằng 16 nhưng thực tế height của progressView vẫn = 2. Vì vậy khi set cornerRadius chúng ta không thể sử dụng frame.height/2 được mặc dù đó là cách tốt nhất đối với các view khác.
if let sublayers = progressBar.layer.sublayers, sublayers.count > 1 {
sublayers[1].cornerRadius = 8.0
}
progressBar.subviews[1].clipsToBounds = true
Nếu để ý kĩ các bạn sẽ thấy progress bar này gồm 2 view chồng lên nhau đó là Progress View và track view. Và theo thứ tự trong lập trình thì thằng đầu tiên là thằng nằm dưới có index = 0. Chúng ta nhìn thấy progress view vì nó nằm trên Track View -> nó có index = 1
Lưu ý: Nó chỉ đúng khi chúng ta không thêm subview, sublayer cho ProgressView. Vậy nên trong trường hợp này chúng ta sẽ ổn.
If let giúp chúng ta handle trường hợp crash app, khi các layer của progress bị remove. (nó thường k xảy ra, nhưng vì an toàn chúng ta nên thêm dòng này)
sublayers[1]: là layer của track view subviews[1]: là trackView
Kết quả thu được thật mỹ mãn 😀
Ngoài ra các bạn có thể sử dụng image cho progress view cũng như trackView để có 1 progress đẹp hơn.
Bước 1: Đăng nhập vào App store connect App store connect là trang quản lý ứng dụng của bạn trên store của apple. Để truy cập vào trang này chúng ta đăng nhập tài khoản Apple ID vào App store connect Đăng nhập thành công chúng ta sẽ có giao diện như hình dưới:
Bước 2: Chọn My Apps để sang màn hình quản lý ứng dụng của bạn.
Bước 3: Chọn + sau đó chọn New App
Bước 4: Điền đầy đủ thông tin liên quan đến ứng dụng của bạn.
Platforms: Ứng dụng bạn tạo chạy trên nền tảng nào?
Name: Đây là tên ứng dụng của bạn
Primary language: Ngôn ngữ mặc định mà người dùng nhìn thấy là gì? Nếu ứng dụng của các bạn chỉ phát hành 1 ngôn ngữ cho 1 quốc gia thì bạn chọn ngôn ngữ mà Ứng dụng đang sử dụng. Nếu ứng dụng của bạn phát hành trên toàn thế giới, mình nghĩ bạn nên chọn là Tiếng Anh vì khi ngôn ngữ máy của người dùng không nằm trong danh sách ngôn ngữ các bạn hỗ trợ nó sẽ hiển thị Tiếng Anh.
Bundle ID: Đây là bundle ID của ứng dụng của bạn. Nó phải khớp với Bundle ID bạn sử dụng trong XCode.
SKU: Viết tắt của Stock-Keeping Unit nó giúp apple quản lý kho ứng dụng của họ khi bạn upload ứng dụng của bạn lên store. Nó phải là duy nhất nên mình hay dùng trùng với Bundle Id.
User Access: Quyền truy cập vào quản lý ứng dụng này
Điền đầy đủ thông tin và bấm vào nút Create để tạo mới ứng dụng.
Bước 4: Chọn loại ứng dụng Bạn hãy chọn category đúng với ứng dụng của bạn
Cập nhật thông tin ứng dụng cho phiên bản mới
Bước 1: Chọn tab iOS App > 1.x Prepare for Submission > Cập nhật thông tin phiên bản mới (nếu đây không phải là bản đầu tiên)
Bước 2: Cập nhật bộ ảnh giới thiệu ứng dụng của bạn. Ảnh phải có định dạng là JPG hoặc PNG. và sử dụng dạng màu RGB. Video Preview chỉ chấp nhận định dạng M4v, MP4 hoặc MOV và không vượt quá 500MB
Apple yêu cầu bạn phải có ýt nhất 5 ảnh giới thiệu về ứng dụng của bạn. Nếu ứng dụng của bạn chỉ hỗ trợ Iphone thì bạn cần chuẩn bị 5 hình cho Iphone 6.5 Display và 5 hình cho Iphone 5.5 Display. Nếu có hỗ trợ Ipad thì cần thêm 5 hình cho Ipad 12.9 Display Cụ thể kích thước các bạn xem ở link này.
Bước 3: Nhập thông tin ứng dụng của bạn
Promotional text: là đoạn text quảng cáo cho phép bạn thông báo tới người dùng truy cập App store của mình về bất cứ tính năng ứng dụng nào mà không yêu cầu gửi cập nhật. Nó sẽ xuất hiện phía trên mô ta của bạn trên appstore và chỉ dành cho khách hàng sử dụng iOS 11 trở lên và macOS 10.13 trở lên.
Keywords: Là chuuỗi những từ khóa mà bạn muốn người dùng tìm thấy ứng dụng của bạn.
Description: Mô tả về ứng dụng của bạn.
Support URL: Link hỗ trợ
Bước 4: Cập nhật thông tin chung của ứng dụng
App store icon: Đây là icon ứng dụng của bạn yêu cầu kích thước 1024×1024
Copyright: Thường là tên cty
sign-in required: Nếu ứng dụng của bạn yêu cầu Login mới sử dụng được hãy tích vào và điền thông tin USername password để nhân viên apple review.
Bước 5: Chọn dạng release
Bạn có thể chọn tự động release ứng dụng khi nhân viên của Apple chấp thuận ứng dụng của bạn. Hoặc chọn ngày để release
Bước 6: Bấm save – Bước quan trọng nhất =))
Bước 7: Chọn file build cho phiên bản này để nhân viên của apple review.
Bâm dấu + để chọn bản build mà bạn muốn apple review
Bản build mà bạn chọn cho apple review chính là bản được xuât hiện trên store.
Nếu bạn chưa up bản nào lên thì hãy đọc tiếp bước dưới đây.
Cách tạo file build trên XCode
Chuẩn bị trước khi Archive:
Bạn cần tăng version của app nếu đây là bản release tiếp theo của bản trước đó: Ví dụ: Nếu bản trên store đang là 1.0 thì version của bản này phải > 1.0
Bạn cần tăng bản build của version app: Với mỗi 1 version apple yêu cầu các build version mới phải > build version cũ. Ví dụ: Nếu Bản testflight của bạn đang là 1.0 (1) thì bạn cần tăng build cho nó lớn hơn(1)
Nếu không để ý các bước chuẩn bị, khi upload lên xcode sẽ thông báo lỗi và lúc đó các bạn sẽ phải archive lại từ đầu. Sẽ mất rất nhiều thời gian nên hãy cẩn thận ở bước này.
Để thay đổi version và build version của app bạn chọn App Target > General
Để Archive ứng dụng bạn chọn Product > Archive Lưu ý: Archive bị disable khi sử dụng device là similator(máy ảo) vì vậy bạn cần chọn máy thật hoặc chọn Generic iOS Device
Khi Archive thành công sẽ hiển thị popup quản lý file Archive. Nếu bạn lỡ tay tắt pop up thì có thể mở lại bằng cách Chọn Window > Organizer
Ở đây bạn chọn Automatically manage signing để xcode tự động tạo hoặc update certificate cho bạn. > Next Hoặc bạn cũng có thế chọn bằng tay
Màn hình review file ipa của bạn được hiển thị ra: Lúc này bấm > Upload
Chờ đợi apple verify file ipa của bạn. Khi này có thể xảy ra 2 trường hợp
Apple trả về lỗi: Copy lỗi paste lên google là có ng trả lời giúp nhé
Apple trả về thành công: Bạn đã đẩy được file lên testflight và phải chờ khoảng 15 phút để file ipa đó có thể hoạt động được. Khi nào file ipa sẵn sàng sẽ có thông báo tới các tài khoản test được đăng ký ở Testflight.
Lúc này bạn quay lại trang App Store connect để kiểm tra trạng thái.
Lúc này hãy đi pha 1 tách trà và chờ đợi. Uống xong trà là nó sẽ xong ấy mà :)). Bao giờ nó chuyển sang Ready to test thì bạn có thể sử dụng để đẩy lên store.
Tuy nhiên để đảm bảo an toàn cho từng bản build, với mỗi bản build này bạn cần xây dụng một file checklist để kiểm tra 1 loạt các tính năng chính của ứng dụng trước khi Submit to review
Tiếp theo bạn quay lại bước 7 ở trên. Chọn file vừa mới upload lên.
Vậy là hoàn tất các bước chuẩn bị. Giờ chúng ta bấm Submit for Review
Vậy là các bạn đã hoàn tất việc đẩy ứng dụng lên store.
Sau 1 khoảng thời gian từ 1 -> x ngày để apple review ứng dụng của ban. Nếu cuối tuần nó sẽ lâu hơn vì cuối tuần là ngày nghỉ nhân viên nó k làm việc Lúc này sẽ có 2 trường hợp xảy ra
Apple reject bản build của bạn: Vì có thể bạn vi phạm chính sách nghiêm ngặt mà apple đã đặt ra. Hãy đọc bài App Store Review Guideline để không bị reject.
Apple Approve bản build của bạn: Xin chúc mừng bạn đã đẩy lên store thành công.
Tổng kết
Vậy là mình đã hướng dẫn các bạn upload thành công 1 ứng dụng lên store. Chúc các bạn thành công!
Như các bạn đã biết App store là chợ ứng dụng của Apple, nơi chứa tất cả những ứng dụng trên toàn thế giới được apple công nhận. Tất cả những ứng dụng muốn đưa lên Appstore đều phải trải qua những bước kiểm duyệt nghiêm ngặt của Apple. Mà trong các công ty chỉ 1 số người có quyền có thể upload ứng dụng lên store thôi. Vì vậy mình muốn viết bài này để chia sẻ cho tất cả các anh em cùng biết.
Giải thích các thuật ngữ
Trước khi đi vào chi tiết mình muốn các bạn nắm rõ các thuật ngữ trước, để đảm bảo các bạn hiểu rõ vấn đề và giúp mọi việc trở nên dễ dàng hơn
.ipa (iOS App store Package) là file lưu giữ ứng dụng iOS. Nó chỉ có thể được cài đặt trên thiết bị iOS.
Archive: Là hành động tạo file để upload lên app store.
Apple ID: là tài khoản của người dùng được Apple cung cấp để đăng nhập trên các sản phẩm của Apple. Đối với lập trình viên chúng ta cần sử dụng tài khoản thông qua trang App Developer. Để có thể thành apple developer bạn phải nâng cấp tài khoản Apple ID của mình. Và chi phí cho tài khoản cá nhân là 99$/năm. Hoặc nếu bạn làm việc trong công ty bạn cũng có thể yêu cầu người có quyền mời bạn vào dự án và cấp quyền cho bạn.
Certificate: Đây là chứng chỉ của Apple. Apple dùng nó để xác nhận xem app đó có thể hoặc còn hoạt động được nữa hay không. Thường thời hạn của các Certificate sẽ là 1 năm. Có 2 kiểu certificate đó là: – Development certificate: Nó giúp bạn build app trên thiết bị thật đã được đăng ký(Giới hạn 100 devices) – Distribution certificate: Nó giúp XCode xác nhận và có thể đẩy file .ipa lên app store connect.
Bundle ID: Nó là duy nhất và định danh ứng dụng của bạn
Device identifiers: là những device được đăng kí sử dụng trong môi trường phát triển(development)
Provisioning profiles: Nó cho phép cài đặt ứng dụng trên môi trường Development và Distribution. Nó bao gồm cả signing certificates, device identifiers và bundle ID. – Development: Nó giúp bạn có thể cài ứng dụng trên các Device đã được đăng kí – Distribution: Nó dùng để đưa app lên Itunes connect -> đưa lên store
App store connect: Đây là trang quản lý ứng dụng của bạn. Đây là nơi mà bạn chuẩn bị các tài liệu, hình ảnh(metadata) và các thông tin liên quan tới ứng dụng của bạn. Nó giúp bạn gửi thông tin này đến bên kiểm duyệt ứng dụng của apple và cho biết trạng thái ứng dụng của bạn.
Các bước chuẩn bị
Để tải ứng dụng lên App store connect chúng ta cần phải chuẩn bị các thông tin liên quan đến ứng dụng như: App ID, Certificates, Provisioning profile …
Bước 1: Chuẩn bị một tài khoản apple id đã được nâng cấp lên Apple Developer.
Để có thể tải ứng dụng lên Store. Apple yêu cầu người dùng phải có tài khoản Developer. Để trở thành Apple Developer chúng ta có 2 cách:
Nâng cấp tài khoản Apple Id thường với giá 99$/năm cho tài khoản cá nhân.
Sử dụng tài khoản Apple Developer của người khác mời vào dự án đang làm.
Sau khi hoàn tất công việc trên chúng ta đăng nhập vào trang Apple Developer
Nếu bạn đung sử dụng XCode có version >= 11 thì chọn Apple Distribution, nêu không thì chọn iOS Distribution( App store Ad Hoc). Sau đó bấm tiếp tục.
Đến đây Apple bắt chúng ta phải tải lên file Certificate signing request. Để tạo file này chúng ta cần mở ứng dụng Keychain Access để tạo.
Một popup sẽ hiện ra, bạn hãy điền đầy đủ thông tin như dưới hình và chọn Save to disk
Điền đầy đủ thông tin như hình và bấm Continue(tiếp tục). Lúc này nó sẽ hiện popup cho chúng ta lưu file này. Lưu ý: Bạn nên tạo các thư mục để quản lý các file certificate cho mỗi một ứng dụng. Nhằm mục đích sau này có thể cần đến.
Bây giờ hãy quay lại trang web và chọn file Certificate signing request vừa tạo. Và bấm Continue( tiếp tục). Vậy là ta đã tạo được Distribution Certificate. Với các Certificate khác, các bạn cũng làm tương tự.
Tiếp theo chúng ta cần mở certificate vừa tạo để tải certificate về cài đặt trên Keychain Access của máy đang sử dụng.
Bấm vào Download để tải về certificate này. Tải về xong bạn bấm double click để cài đặt nó vào Keychain Access.
Lưu ý: Bạn nên export private key của certificate này và lưu nó cẩn thận. Nếu không có private key các device khác sẽ không sử dụng được Certificate này.
Bước 3: Tạo App ID
Với mỗi một ứng dụng trên store đều phải có một App Id nhằm định danh cho nó khi ở trên Store.
Mở tab Identifiers > Bấm dấu +
Một trang mới xuất hiện, bạn chọn App IDs và bấm Continue(Tiếp tục)
Trang đăng ký App ID xuất hiện Bạn hãy điền các thông tin ứng dụng của bạn vào các ô trống và bấm Continue (Tiếp tục) để hoàn thành việc tạo App ID.
Bước 4: Tạo Provisioning Profile – Distribution
Mở tab Profiles > Chọn dấu + để tạo mới provisioning profile
Lúc này màn hình Register A New Provisioning profile được hiển thị. Bạn muốn tạo loại profile nào thì tích vào mục đó. Trong trường hợp này chúng ta cần tạo profile để upload app lên store vì vậy chúng ta sẽ chọn mục Distribution – App Store như hình dưới và bấm Continue(Tiếp tục):
Màn hình Generate A Provisioning Profile được hiển thị: Bạn muốn tạo Profile cho ứng dụng nào thì chọn AppID của ứng dụng đó và bấm Continue
Tiếp đến bạn cần chọn Distribution certificate mà bạn đã tạo ở Bước 2. Trong trường hợp bạn tạo nhiều distribution Certificate nó sẽ hiển thị dưới dạng list cho bạn chọn. Chọn certificate và bấm Continue
Vậy là bạn đã tạo thành công profile cho distribution phục vụ cho việc tải ứng dụng lên App store connect.
Tải về profile bạn vừa tạo và bấm double click để cài đặt.
Tổng kết
Trong bài viết này mình đã giúp các bạn làm quen với một số keywords và cách để tạo các file certificates, app id, profile …. Đó là những bước đầu để chuẩn bị cho việc đưa ứng dụng của các bạn lên store.
Giao diện người dùng(User Interface) của một ứng dụng hiện đại là một cấu trúc phức tạp. Nó liên quan đến các thành phần UI, bố cục và bản thiết kế dẫn đến quá trình gỡ lỗi(debugging) trở nên khá phức tạp. Việc biết các công cụ có sẵn giúp bạn giảm hàng giờ ngồi gỡ lỗi và làm cho việc tìm ra vấn đề trở nên đơn giản hơn.
Trong bài viết này mình sẽ giới thiệu về các công cụ gỡ lỗi giao diện do Apple cung cấp.
View hierarchy debugger cung cấp khả năng kiểm tra và hiểu hệ thống phân cấp UI. Các thông tin bao gồm Phân cấp ứng dụng(app’s hierarchy), các view, các ràng buộc và view controller được hiển thị dưới dạng danh sách phân cấp. Object inspector và size inspector và thậm chí là mô hình 3D có thể tương tác được. Xịn sò vl 😀
Nó được đặt ở thanh debug. Để mở tính năng này bạn bấm vào biểu tượng được khoanh đỏ hình dưới đây. Khi ở chế độ này XCode sẽ dừng ứng dụng của bạn ở trạng thái hiện tại.
Mở debug view hierarchy
Tính năng này có thể sử dụng được cho iOS, tvOS, macOS, máy ảo(simulator) và thiết bị thật.
2. lldb
Nếu bạn thích console hơn là các công cụ trực quan thì UIKit cũng cung cấp các APIs logging rất ổn. Có một vấn đề nhỏ đó là những APIs private. Nhưng chúng ta cũng không dùng debug cho các bản release.
Nó sẽ hiển thị các thông tin bố cục, bản vẽ, CALayer và các trường cụ thể bổ sung.
-[UIViewController _printHierarchy]
Tương tự UIViewController cũng hiển thị các thông tin như dưới đây:
(lldb) po [viewController _printHierarchy]
<UISplitViewController 0x7fb23a605920>, state: disappeared, view: <UILayoutContainerView 0x7fb23d80dc60> not in the window
| <UINavigationController 0x7fb23b817000>, state: disappeared, view: <UILayoutContainerView 0x7fb23d80e280> not in the window
| | <MasterDetailTemplate.MasterViewController 0x7fb23a606140>, state: disappeared, view: <UITableView 0x7fb23b05ac00> not in the window
+ <UINavigationController 0x7fb23b01d600>, state: appeared, view: <UILayoutContainerView 0x7fb23a503150>, presented with: <_UIFullscreenPresentationController 0x7fb23d808f20>
| | <MasterDetailTemplate.ModalViewController 0x7fb23a409660>, state: appeared, view: <UIView 0x7fb23a51dfc0>
| là biểu tượng cho biết các child view controllers
+ đại diện cho Modal presentation
_printHierarchy là phương thức đặc biệt hữu ích khi được gọi triên rootViewCOntroller của UIWindow:
po [[[UIWindow keyWindow] rootViewController] _printHierarchy]
Các phương thức ở trên đều dành cho Objective-C. Nếu bạn đang sử dụng swift thì cần tạo một số extension như sau:
#if DEBUG
extension UIWindow {
class var key: UIWindow {
let selector: Selector = NSSelectorFromString("keyWindow")
let result = UIWindow.perform(selector)
return result?.takeUnretainedValue() as! UIWindow
}
}
extension UIView {
var recursiveDescription: NSString {
let selector: Selector = NSSelectorFromString("recursiveDescription")
let result = perform(selector)
return result?.takeUnretainedValue() as! NSString
}
}
extension UIViewController {
var printHierarchy: NSString {
let selector: Selector = NSSelectorFromString("_printHierarchy")
let result = perform(selector)
return result?.takeUnretainedValue() as! NSString
}
}
#endif
Khi tạo xong các extension này chúng ta có thể sử dụng nó ở console bằng cách:
po view.recursiveDescription
po UIWindow.key.rootViewController!.printHierarchy
3. Instruments – Core Animation
Kiểm tra cấu trúc UI để tối ưu hóa hiệu suất. Điều đầu tiên chúng ta cần làm là đo khung hình mỗi giây. Con người có cảm nhận ứng dụng chạy mượt khi nó có thể hoạt động ở ~ 60FPS (Frames per second). Và đó chính là mục đích của chúng ta.
Lưu ý: Nó không hoạt động trên Simulator
Để mở chúng ứng dụng này chúng ta sử dụng tính năng search(command + space) đánh từ khóa Instruments:
Mở ứng dụng Instruments lên và chọn core animation
Core Animation đo hiệu năng đồ họa của ứng dụng. Trong ví dụ này, mình chỉ tập trung vào rendering performance. Ứng dụng của mình hiện tại là một UITableView với các custom cell. Bây giờ nó chỉ hiển thị section và row.
Khi cuộn mình nhận thấy nó hơi giật. Cấu hình ứng dụng với Instruments cho thấy hiệu suất cách xa mức 60 FPS. Khi cuộn (từ giây thứ 4->23) hiệu suất giảm xuống còn ~44 FPS giảm 16 so với mục tiêu đặt ra.
Tôi nghi ngờ vấn đề ở đây liên quan đến cell rendering. Với tính năng Color Blended Layers debug được bật, ta có thể hiểu rõ hơn về những gì đang xảy ra.
Color blended layer chỉ ra các view layers đang được vẽ chiingf lên nhau.
Để hiểu tại sai nó lại là vấn đề, chúng ta cần biết hình ảnh cuối cung được tạo ra như thế nào. Pixel cuối cùng mà người dùng nhìn thấy là một bố cục pha trộn các pixel trên cùng và bên dưới cùng tọa độ. Nó có nghĩa là mọi view layer phải được render để tạo hình ảnh cuối cùng. Khi view layer mờ đục, blending(Pha trộn) có thể được tối ưu hóa bằng cách không vẽ các layer bên dưới.
Để bẻ cong góc và đổ bóng cho View, tôi sử dụng CALayer properties:
Điều này làm cho các phần view của tôi có các layer trong suốt, nó gây ra việc blending không cần thiết. Bởi vì background color được tô, nên rendering có thể được tối ưu. Ta sẽ set một solid background cho View của mình và sử dụng hình nên mà không có alpha:
let cardView = UIImageView(image: UIImage(named: "cardBackground"))
Đây là một thủ thuật đơn giản giúp chúng ta giải quyết việc bị blending.
Lúc này hiệu suất của ứng dụng đã tốt hơn, cho cảm giác cuộn rất mượt.
Bạn cũng có trể truy cập nhanh vào Color Blended Layers từ iOS Simulator.
4. Pixie
Pixie giúp bạn thực hiện thiết kế một cách hoàn hảo. Công cụ này cho phép bạn kiểm tra từng pixel của ứng dụng theo đúng nghĩa đen.
Pixie có thể được tải từ website của Apple. XCode > Open Developer Tool > More Developer Tools… trong công cụ bổ xung cho XCode.
Tổng kết
Mình hi vọng bài này giúp các bạn có thể giúp các bạn tận dụng tool debug của apple để làm việc tốt hơn 😀
UIScrollView là một view tạo constraint khá là phức tạp và khó hiểu với người mới. Thật không may là nó lại được sử dụng trong khá nhiều trong ứng dụng của bạn. Nhưng nó sẽ trở nên đơn giản hơn nếu bạn hiểu được bản chất của nó.
UIScrollView là một view mà nó cho phép cuộn và phóng to nội dung của nó.
Khi tạo constraint cho scroll view bạn cần tạo constraint cho 2 bước: – Bước 1 là cần phải tạo các constraints cố định khung(kích thước và vị trí) của scroll view so với super view của nó. – Bước 2 là cần phải tạo các constraint cho các view trong scrollview.
1.1 Tạo constraint cho UIScrollView không sử dụng Content Layout Guide
Sử dụng trong trường hợp bạn sử dụng bản XCode version < 11 hoặc bạn sử dụng XCode 11 nhưng không muốn dùng Content Layout Guide của UIScrollView.
Giờ chúng ta kéo 1 scrollView vào và tạo constraint nó với superview như hình dưới:
Vậy là ta đã hoàn thành bước 1: tạo constraint xác định vị trí, kích thước của Scroll View. Trong trường hợp này mình đang để scrollview có kích thước bằng kích thước của màn hình.
Tiếp theo chúng ta đến bước 2 tạo constraint cho các view trong scroll view. Chúng ta cần kéo vào 1 UIView để chứa tất cả các View. Lúc này chúng ta sẽ tạo constraint cho View này bằng cách kéo chuột phải từ view đó vào scrollview và giữ Shift để add nhiều constraint như hình dưới:
Việc Fix equal height, equal width với scroll view giúp các bạn xác định vị trí cũng như kích thước của nội dung scrollview.
Giờ chúng ta có thể kéo các View mình muốn vào trong view này và sắp xếp chúng theo ý muốn. Ở trường hợp này mình muốn kéo vào 1 label và 1 button. Nên mình sẽ constraint theo như hình phía dưới.
Tiếp theo, do trong trường hợp này mình làm scrollview cuộn theo chiều dọc nên mình sẽ bỏ constraint equal height của view với scrollview đi. Khi đó ta cần sửa lại chỉ số constraint bottom của button sao cho phù hợp với thiết kế, giả sử trường hợp này mình sẽ để là 40 points. Ta sẽ được kết quả như hình:
Tiếp đến các bạn cần tìm constraint bottom của View Content và sửa lại giá trị của nó về giá trị mình mong muốn( thường sẽ để là 0) Constraint này đại diện cho khoảng cách bottom giữa scrollview và contentview khi mà content view có size lớn hơn size của scroll view.
Vậy là xong. Bước cuối cùng chúng ta test lại scroll view bằng cách set 1 đoạn text dài cho Label. kết quả sẽ được như hình dưới:
1.2 Tạo constraint cho UIScrollView với Content Layout Guide
Từ XCode 11 trở đi Apple đã cho thêm 1 thuộc tính Content Layout Guide cho UIScrollView giúp mọi người tạo constraint cho nó 1 cách dễ dàng hơn. Khi bạn kéo vào 1 UIScrollView ở XCode 11 nó sẽ mặc định được bật tính năng này. Khi này bên trong scroll view sẽ có 2 layout guide là Content Layout guide và Frame Layout Guide
Nó khác với cách tạo 1.1 là thay vì nó constraint trực tiếp với ScrollView thì nó lại constraint với các Layout guide của scroll view.
Content Layout guide: Chọn ContentView sử dụng chuột phải kéo vào Content layout guide và tạo các constraint ContentView với content layout guide leading, top, trailing, bottom như sau:
Frame Layout guide: Chọn content View sử dụng chuột phải kéo vào Frame Layout guide và tạo các constraint equal width nếu muốn cuộn dọc hoặc equal height nếu muốn cuộn ngang.
Còn lại chúng ta vẫn làm giống ở mục 1.1.
2. UILabel
Thông thường 1 UILabel chỉ cần 2 constraint để xác định được vị trí cũng như kích thước của nó. Thế nên rất nhiều bạn khi tạo constraint cho UILabel thường bị thiếu constraint, dẫn đến trường hợp khi build app lên bị vỡ layout.
2.1 Nên tạo ít nhất 4 Constraint cho UILabel
UILabel là view có thể tự thay đổi kích thước của nó dựa trên content size (text, font ..) của nó. Vì vậy nếu bạn không tạo đủ constraint cần thiết cho UILabel trong một số trường hợp nội dung của UILabel sẽ không hiển thị đúng như mong đợi.
Nếu bạn chỉ sử dụng 2 constraint sẽ dẫn đến trường hợp khi nôi dung của label dài ra nó sẽ bị tràn ra ngoài màn hình.
2.2 Nên set thêm constraint height cho các UILabel nếu nó làm nhiệm vụ giữ trục phát triển View
Trong trường hợp UILabel làm nhiệm vụ giữ trục của view mà nó được gán giá trị empty sẽ dẫn đến trường hợp bị vỡ layout vì khi UILabel có text = “” (empty) height của nó sẽ = 0 vì thế ta cần tạo constraint cho nó và để giá trị là greater than or equal (>=). Khi đó các label giữ trục của view sẽ không làm vỡ layout nữa vì giá trị height nhỏ nhất của nó hiện tại không phải là 0 nữa.
Các bạn theo dõi sự khác nhau của 2 hình dưới đây.
Có set height >=Không set height
3. UIButton
UIButton cũng là một View có thể tự thay đổi kích thước dựa trên nội dung của nó. Vì vậy chúng ta cũng cần lưu ý khi tạo constraint cho nó. Đối với buton chúng ta chỉ cần 2 constraint là có thể xác định vị trí cũng như kích thước của nó. Nhưng để nó có thể hiển thị đúng trong nhiều trường hợp thì chúng ta cần nhiều hơn 2 constraint.
Không nên set width cho button có chứa text
Vì UIButton nó sẽ tự thay đổi kích thước theo nội dung bên trong, nên khi text dài ra hoặc ngắn đi nó sẽ tự thay đổi kích thước của button cho phù hợp. Nếu chúng ta gán constraint width cho nó thì dễ dẫn đến trường hợp khi text dài ra button sẽ hiển thị mất chữ. Vì vậy thay vì các bạn set constraint width cho UIButton để đạt được kết quả như file design thì chúng ta sẽ sửu dụng thuộc tính insets để căn chỉnh khoảng cách nội dung của UIButton.
Ở đây mình sử dụng content insets để căn khoảng cách của nội dung button với các cạnh của nó.
4. Priority: Độ ưu tiên khi các View
Với mỗi view đều có 2 loại Priority đó là: – Content Hugging Priority (Vertical, horizontal) – Content Compression Resistance Priority (Vertical, horizontal)
Content Hugging Priority
Là độ ưu tiên ôm nội dung của View. View nào có độ ưu tiên của nó lớn hơn thì nó sẽ ưu tiên nội dung của view đó hiển thị đúng với các chỉ số. Các view lân cận có độ ưu tiên nhỏ hơn sẽ phải tự dãn content để lấp dầy khoảng trống còn lại theo chiều ngang hoặc dọc. Nó sẽ hoạt động nếu nội dung của các chiều đang không lấp đầy khoảng trống có sẵn trong super view.
Theo dõi hình ảnh dưới đây để hiểu rõ hơn:
Hình này Label có Hugging priority lớn hơn của Button. Vì vậy nó sẽ ưu tiên ôm nội dung của Label và dãn button.Trường hợp này thì Button có Hugging priority lớn hơn của Label. Nên nó sẽ ưu tiên ôm nội dung của Button và dãn Label.
NOTE: Nếu Priority của 2 View bằng nhau nó sẽ sảy ra lỗi constraint. Vì vậy các bạn cần xác định được View nào cần độ ưu tiên cao hơn ở các trường hợp khác nhau.
Tuy nhiên nó chỉ đúng khi nội dung của các view không chiếm hết khoảng không còn lại ở super view theo chiều của priority. Khi nội dung của các view vượt quá khoảng không có sẵn thì chúng ta cần sử dụng Content compression resistance priority để giải quyết.
Content compression resistance priority
Đây là thuộc tính thể hiện độ ưu tiên chống lại việc nén nội dung của View. Có nghĩa là View nào có priority lớn hơn sẽ được ưu tiên hiển thị nội dung của nó để lấp khoảng trống còn lại trong super view trước, sau đó nếu còn khoảng trống thì các view còn lại có priority nhỏ hơn sẽ được lấp vào, nếu hết khoảng trống nó có thể sẽ biến mất nếu không có constraint.priority lớn hơn(Priority của các constraint mặc định là 1000). Priority này sẽ hoạt động khi nội dung của các view được lấp đầy hoặc vươti quá size của super view.
Theo dõi hình dưới đây để hiểu rõ hơn:
Độ ưu tiên chống nén của UILabel cao hơn Button nên nội dung của button sẽ không hiển thị đầy đủ Trong trường hợp này độ ưu tiên chống nén nội dung của UILabel thấp hơn nên Nội dung của button được hiển thị đầy đủ, còn UILabel trong trường hợp này sẽ mất chữ nếu có line = 1. Vì vậy ở đây mình để line = 0 để label tự co dãn
Tổng kết
Mình hi vọng bài viết sẽ giúp cho các bạn có thêm 1 chút tự tin khi làm việc với Auto Layout 😀
Bài viết này mình sẽ giới thiệu với các bạn về một trường hợp đặc biệt trong Auto Layout đó là Stack View. Stack view là một công cụ mạnh mẽ để giúp bạn nhanh chóng cũng như dàng thiết kế giao diện người dùng của bạn.
Stack view cho phéo bạn tận dụng sức mạnh của Auto Layout, tạo giao diện người dung có thể tự động thích ứng với hướng của thiết bị(Device’s orientation), kích thước màn hình, và mọi thay đổi trong không gian có sẵn. Stack view quản lý layout của tất cả các Views trong nó bằng thuộc tính arrangedSubviews. Độ chính xác của layout phụ thuộc vào các thuộc tính của nó như là: Axis, alignment, distribution, spacing và các thuộc tính khác.
Hình ảnh mô tả các thuộc tính đặc trưng của stack view
Các thuộc tính của stack view
Axis: Định nghĩa trục phát triển của stack view. Có 2 kiểu stack view đó là Vertical và horizontal.
Alignment: Định nghĩa việc căn chỉnh của các View được sắp xếp vuông góc với trục của stack view. Nó có các giá trị như sau:
UIStackView.Alignment.fill: Làm cho các View bên trong stack view lấp đầy khoảng trống có sẵn trong stack view.
UIStackView.Alignment.top: Dùng cho horizontal stack view, nó căn chỉnh cạnh trên của các View trong stack view theo cạnh trên của của nó.
UIStackView.Alignment.leading: Dùng cho vertical stack view. Nó căn chỉnh các cạnh trái của các view trong stack view với cạnh trái của nó.
UIStackView.Alignment.firstBaseline: Dùng trong horizontal stack view. Nó căn chỉnh các view trong stack view dựa trên đường baseline đầu tiên của chúng.
UIStackView.Alignment.lastBaseline: Dùng cho horizontal stack view. Nó căn chỉnh các view trong stack view dưa trên đường baseline cuối cùng của chúng.
UIStackView.Alignment.center: Nó căn giữa các view trong stack view theo chiều của nó.
UIStackView.Alignment.trailing: Dùng cho vertical stack view, căn các cạnh phải của view trong stack view với cạnh phải của stack view.
UIStackView.Alignment.bottom: Dùng cho horizontal stack view. Căn chỉnh các view trong stack view với cạnh dưới của nó.
Distribution: là thuộc tính xác định bố cục của các view được sắp xếp theo chiều của stack view.
Các thuộc tính của nó như sau:
UIStackView.Distribution.fill: Nó giúp các view trong stack view lấp đầy không gian có sẵn trong stack view. Khi các view không xếp vừa stack view, nó sẽ thu nhỏ các view dựa trên độ ưu tiên của kháng nén của chúng (Compression resistance priority). Nếu các views được sắp xếp không lấp đầy được stack view, nó sẽ kéo dài các view theo hugging priority của chúng.
UIStackView.Distribution.fillEqually: Thay đổi kích thước các view bên trong stack view sao cho tất cả các view có cùng kích thước theo chiều của nó và lấp đầy không gian có sẵn theo chiều của stack view. Ví dụ: Horizontal stack view thì các view sẽ bằng nhau về chiều rộng, vertical stack view thì bằng nhau về chiều cao.
UIStackView.Distribution.fillProportionally: Thay đổi kích thước của các view trong stack view sao cho chúng lấp đầy các không gian có sẵn theo trục của stack view. Các view được thay đổi kích thước theo tỉ lệ dựa trên kích thước nội dung bên trong của chúng dọc theo chiều của stack view.
UIStackView.Distribution.equalSpacing: Các view trong stack view sẽ được sắp xếp sao cho khoảng cách giữa chúng bằng nhau. Nếu các view được sắp xếp không vừa stack view nó sẽ thu nhỏ các View dưa trên độ ưu tiên kháng nén của chúng(Compression resistance priority).
UIStackView.Distribution.equalCentering: Làm cho các view strong stack view được sắp xếp sao cho chúng có khoảng cách từ trung tâm bằng nhau theo chiều của stack view. Trong khi duy trì thuộc tính spacing(khoảng cách) giữa các view. Nếu các view được sắp xếp không vừa stack view, nó sẽ thu hẹp khoảng cách cho đến khi đạt khoảng cách tối thiểu được xác định bới thuộc tính khoảng cách của nó. Nếu các view vẫn không phù hợp, stack view sẽ thu nhỏ các view bên trong nó dựa trên thuộc tính Compression resistance priority.
Spacing: Là thuộc tính định nghĩa khoảng cách các cạnh liền kề của các view trong stack view.
Để làm việc với stack view hiệu quả các bạn cần hiểu các thuộc tính của nó. Khi bạn thành thạo nó rồi sẽ giúp bạn tiết kiệm khá nhiều thời gian khi auto layout.
2. Ví dụ đơn giản về Stack View
Ở ví dụ này mình sử dụng stack view bố trí layout theo hàng dọc để xắp xếp Label, ImageView và Button.
2.1 Mô tả Views và Constraints
Sử dụng Interface Builder và kéo vào một vertical stack view. Sau đó các bạn kéo các Label, Image View và Button vào trong stack view đó như hình dưới.
Lúc này stack view của chúng ta chưa có constraint nên chúng ta cần gán constraint cho stack view như sau:
Stack View.Leading = Superview.LeadingMargin
Stack View.Trailing = Superview.TrailingMargin
Stack View.Top = Top Layout Guide.Bottom + Standard
Bottom Layout Guide.Top = Stack View.Bottom + Standard
Việc này giúp cho stack view của chúng ta xác định được vị trí và kích thước của nó. Trong trường hợp này stack view của bạn sẽ có kích thước full màn hình.
2.2 Cài đặt các thuộc tính cho stack view
Mở tab Attributes Inspector và set các thuộc tính cho stack view như sau:
Stack
Axis
Alignment
Distribution
Spacing
Stack view
Vertical
Fill
Fill
8
Axis: Đại diện cho chiều hướng của stack view, Vertical(xếp theo chiều dọc)
Alignment: Đại diện cho việc căn chỉnh, Fill(Điền đầy stack view theo chiều của nó)
Distribution: Đại diện cho phân chia các view trong stack view. Ta gán giá trị Fill cho nó nhằm mục đích làm các view được lấp đầy các khoảng trống của stack view.
Spacing: Giá trị được gán = 8 nhằm tạo khoảng cách giữa các View trong stack view cách đều nhau 1 khoảng = 8 points
Tiếp đến chúng ta cần set thuộc tính của image view như sau:
View
Attribute
Value
Image View
Image
Chọn ảnh mà bạn muốn
Image View
Mode
Aspect fill
Cuối cùng chúng ta mở Size inspector lên để gán lại các giá trị Priorities cho Image View:
Name
Vertical hugging
Vertical registance
Image view
249
749
Vì chúng ta sử dụng distribution = Fill nên nó sẽ thu nhỏ các View dựa trên độ ưu tiên kháng nén(Compression registance priority) của chúng. Đó là lí do tại sao chúng ta phải giảm độ ưu tiên theo chiều dọc của Image View.
Vậy là ta đã có được kết quả giống như trong hình ở trên. Để tạo được kết quả như trên mà không dùng stack view chúng ta sẽ phải tạo các constraint cho cả 3 view là label, image view và button. Nhưng với stack view mọi chuyện đã đơn giản hơn nhiều :D.
Ở phần 3 chúng ta đã được làm quen với việc tạo constraints bằng code. Vậy nên bài viết này mình sẽ chia sẻ với các bạn về việc làm sao để tạo constraints bằng code dễ dàng hơn, tiết kiệm thời gian hơn. Và làm thế nào để debug UI.
Việc sử dụng extension để mở rộng các class giúp chúng ta có thể tạo constraint dễ dàng và tiết kiệm nhiều thời gian. Việc của các bạn là nghĩ và tạo ra các func có thể hay được sử dụng.
1. UIView
Ví dụ chung ta có thể tạo ra một func trong extension UIView giúp chúng ta tạo các constraints liên kết với SuperView như sau:
extension UIView {
/// Returns a collection of constraints to anchor the bounds of the current view to the given view.
///
/// - Parameter view: The view to anchor to.
/// - Returns: The layout constraints needed for this constraint.
func constraintsForAnchoringTo(boundsOf view: UIView) -> [NSLayoutConstraint] {
return [
topAnchor.constraint(equalTo: view.topAnchor),
leadingAnchor.constraint(equalTo: view.leadingAnchor),
view.bottomAnchor.constraint(equalTo: bottomAnchor),
view.trailingAnchor.constraint(equalTo: trailingAnchor)
]
}
}
Từ đây các bạn có thể tha hồ nghĩ thêm các func để giúp cho việc tạo constraints của mình trở nên đơn giản và tiết kiệm thời gian hơn nhiều so với việc hì hục ngồi code.
1.2 Xử lí ưu tiên
Khi bạn phải đặt độ ưu tiên cho các constraints của mình để tránh phá vỡ các constraint khác. Bạn có thể sẽ cần đến extension này:
extension NSLayoutConstraint {
/// Returns the constraint sender with the passed priority.
///
/// - Parameter priority: The priority to be set.
/// - Returns: The sended constraint adjusted with the new priority.
func usingPriority(_ priority: UILayoutPriority) -> NSLayoutConstraint {
self.priority = priority
return self
}
}
extension UILayoutPriority {
/// Creates a priority which is almost required, but not 100%.
static var almostRequired: UILayoutPriority {
return UILayoutPriority(rawValue: 999)
}
/// Creates a priority which is not required at all.
static var notRequired: UILayoutPriority {
return UILayoutPriority(rawValue: 0)
}
}
Độ ưu tiên của constraint là phần khá là hay ho, nên mình sẽ dành một mục ở bài viết tiếp theo.
1.3 Tạo Auto layout property wrapper
Khi auto layout bằng code, để tránh việc phải viết đi viết lại nhiều lần dòng code dưới đây: translatesAutoresizingMaskIntoConstraints = false
Chúng ta sẽ tạo ra Property Wrapper như dưới đây:
@propertyWrapper
public struct UsesAutoLayout<T: UIView> {
public var wrappedValue: T {
didSet {
wrappedValue.translatesAutoresizingMaskIntoConstraints = false
}
}
public init(wrappedValue: T) {
self.wrappedValue = wrappedValue
wrappedValue.translatesAutoresizingMaskIntoConstraints = false
}
}
final class MyViewController {
@UsesAutoLayout
var label = UILabel()
}
Lưu ý: Nó không sử dụng được cho biến Local
Giới thiệu về Snapkit
Nếu như các bạn lười nghĩ và viết các extension để hỗ trợ cho việc tạo constraint bằng code. Thì chúng ta lại có một giải pháp khác là dùng của thằng khác :D. Của ăn sẵn nhiều khi cũng tốt mà. Tiện đây mình xin giới thiệu với các bạn về thư viện Snapkit một thư việt rất mạnh về Auto layout bằng code. Giúp chúng ta xử lí Autolayout một cách đơn giản, gọn gàng và tiết kiệm khá nhiều thời gian.
Ưu điểm – Code nhìn gọn gàng, dễ hiểu hơn – Tiết kiệm thời gian khi code
Nhược điểm – Sẽ có những thành viên không sử dụng snapkit mà dùng API default của apple dẫn đến thời gian đầu làm việc gặp khó khăn.
Cài đặt
CocoaPods, Carthage hoặc ném nó vào project của bạn. Tham khảo link này nhé.
Cách sử dụng
1.Tạo constraint
func makeConstraintBySnapkit1() {
let iView = UIView()
let oView = UIView()
iView.backgroundColor = .red
oView.backgroundColor = .blue
view.addSubview(oView)
oView.addSubview(iView)
oView.snp.makeConstraints { (make) in
make.width.height.equalTo(200)
make.center.equalToSuperview()
}
iView.snp.makeConstraints { (make) in
make.top.leading.equalToSuperview().offset(20)
make.bottom.trailing.equalToSuperview().offset(-20)
}
}
Để tạo được constraint có kết quả như hình này bằng code sử dụng API mặc định của apple chúng ta sẽ phải viết khá nhiều dòng code.
2. Giữ reference của constraint và thay đổi nó giá trị của nó khi cần.
var myConstraint: Constraint?
iView.snp.makeConstraints { (make) in
self.myConstraint = make.top.leading.equalToSuperview().offset(20).constraint
make.bottom.trailing.equalToSuperview().offset(-20)
}
myConstraint?.update(offset: 40)
3. Update constraint
Update constraint giúp chúng ta update tùy ý bất kể 1 constraint nào đó của View. Để update constraint ta chỉ cần viết như sau:
iView.snp.updateConstraints { (edit) in
edit.top.leading.equalToSuperview().offset(0)
}
Kết quả thu được sẽ là:
4. Remake constraint
Khác với updateContraint, remake constraint sẽ remove toàn bộ constraints cũ đi và bạn sẽ tạo lại constraint từ đầu như hàm makeConstraint. Câu lênh của nó sẽ như sau:
iView.snp.remakeConstraints { (remake) in
remake.top.leading.equalToSuperview().offset(20)
remake.bottom.trailing.equalToSuperview()
}
Kết quả thu được
Để có thể thành thạo sử dụng Auto Layout và Snapkit chúng ta chỉ có 1 cách đó là làm nhiều, nhiều nữa và nhiều mãi thế thôi =)) Cố gắng lên anh em :))
Tổng kết
Mình hi vọng bài viết này sẽ giúp các bạn phần nào sử dụng Auto Layout dễ dàng và tiết kiệm thời gian hơn.
Bài viết này của mình có 2 nội dung chính. Đầu tiên mình sẽ nói về cấu tạo của một constraint để các bạn có thể hình dung được nó hoạt động như thế nào. Thứ hai mình sẽ nói về việc làm thế nào để tạo một constraint sử dụng code mà không cần dùng đến Interface buider.
Layout của view hierarchy được định nghĩa là một chuỗi các phương trình tuyến tính. Mỗi ràng buộc đại diện cho một phương trình duy nhất. Mục tiêu của bạn là khai báo một loạt các phương trình có một và chỉ một giải pháp khả thi.
Giờ chúng ta sẽ xem cấu tạo của một constraint nó như nào nhé:
Constraint này nói rằng cạnh trái của view màu đỏ(Red view’ leading edge) phải là 8 points sau cạnh phải của view màu xanh(Blue view’s trailing edge). Phương trình của nó có các phần như sau:
Item 1: Đây là phần đầu tiên trong phương trình, trong trường hợp này nó là View màu đỏ. Phần này phải là một View hoặc là một layout guide.
Attribute 1: Là thuộc tính được constraint trên Item 1. Trường hợp này nó là cạnh trái của Red View(Red view’s leading edge).
Relationship: Mối quan hệ giữa bên trái và bên phải của phương trình. Giá trị của relationship có thể thuộc 1 trong 3 giá trị: bằng, lớn hơn hoặc nhỏ hơn. Trong trường hợp này bên trái và bên phải bẳng nhau.
Multiplier: Hệ số giá trị của Attribute được nhân với số này. Trong trường hợp này hệ số nhân là 1.0. Trong các trường hợp mặc định hệ số này là 1.0, nên ta có thể bỏ đi nếu không muốn thay đổi giá trị này.
Item 2: Là phần thứ 2 trong phương trình. Trong trường hợp này nó là Blue View. Không giống với Item 1, nó có thể để trống.
Attribute 2: Là thuộc tính được constraint trên Item 2. Trong trường hợp này là cạnh phải của BlueView. Nếu Item 2 để trống thì Attribute 2 sẽ không tồn tại
Constant: Một hằng số bù trừ, trong trường hợp này nó là 8.0. Giá trị này được thêm vào giá trị của thuộc tính 2(Value of Attribute 2).
Hầu hết các constraint xác định một mối quan hệ giữa hai items trong giao diện người dùng. Những constraint này có thể đại diện cho các View hoặc Layout guide. Các Constraint cũng có thể xác đinh mối quan hệ giữa hai thuộc tính khác nhau của một item. Ví dụ: bạn có thể đặt Aspect ratio giữa chiều cao của 1 item với chiều rộng của nó. Bạn cũng có thể gán giá trị hằng số cho chiều cao hoặc chiều rộng của Item đó. Khi làm việc với giá trị Constant, Item 2 được để trống thì attribute 2 sẽ được gán là không phải thuộc tính và multiplier được gán là 0.0
Auto Layout Attributes
Trong Auto Layout, các thuộc tính được xác định là một tính năng có thể được ràng buộc(Constraint). Thông thường, nó bao gồm 4 cạnh: Trên(Top), Dưới(bottom), Trái(leading), Phải(trailing), cũng như chiều cai, chiều rộng và căn giữa theo chiều dọc và ngang. Các text cũng có một hoặc nhiều thuộc tính baseline.
Auto layout constraints cho phép chúng ta tạo ra các view tự động điều chỉnh theo các Size Class và Vị trí khác nhau. Các constraint sẽ đảm bảo view của bạn điều chỉnh phù hợp với bất kỳ thay đổi kích thước nào mà không phải cập nhật thủ công các khung hoặc vị trí của nó.
Tuy nhiên, ngoài việc sử dụng Interface Builder để Auto Layout thì chúng ta có thể sử dụng code để Auto Layout.
Việc viết AutoLayout có một số ưu điểm và nhược điểm như sau: Ưu điểm: – Dễ dàng merge code khi dùng Git – Dễ debug – Các constraint có thể dễ nhìn hơn Nhược điểm: – Không có đại diện trực quan. Việc này khiến bạn phải tưởng tượng thay vì như IB cho ta nhìn thấy Views của mình trên màn hình ở nhiều kích thước khác nhau. – Bạn có thể sẽ phải viết nhiều code layout lên trên ViewController của mình.
Theo mình để có thể làm tốt Auto Layout bằng code thì mình nghĩ các bạn nên sử dụng thành thạo Interface Builder trước. Việc kết hợp giữa Interface Builder và constraint trong code cũng có thể là một giải pháp tốt. Tuy nhiên, hãy luôn nhớ rằng việc đó sẽ khiến nó trở nên khó hiểu hơn.
Tạo constraints bằng cách sử dụng Layout Anchors
Việc đầu tiên chúng ta cần làm là phải set thuộc tính translatesAutoresizingMaskIntoConstraints thành false. Việc này nhằm mục đích ngăn các view’s auto-resizing mask được chuyển sang Auto Layout constraints và ảnh hưởng đến các constraint của bạn.
Tiếp theo chúng ta sẽ bắt đầu tạo một mảng chứa các constraints. Trong mảng này chúng ta sẽ định nghĩa các constraints mà bạn muốn gắn nó cho một View nào đó.
Trong trường hợp này mình muốn tạo ra 1 view màu đỏ có width = 200, height = 200 và căn giữa màn hình. Vì vậy mình sẽ tạo ra một mảng constraint như dưới đây.
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
let myView = UIView(frame: CGRect(x: 100, y: 100, width: 50, height: 50))
myView.backgroundColor = .red
view.addSubview(myView)
myView.translatesAutoresizingMaskIntoConstraints = false
let constraints = [
myView.centerYAnchor.constraint(equalTo: view!.centerYAnchor),
myView.centerXAnchor.constraint(equalTo: view!.centerXAnchor),
myView.widthAnchor.constraint(equalToConstant: 200.0),
myView.heightAnchor.constraint(equalToConstant: 200.0)
]
NSLayoutConstraint.activate(constraints)
}
Đó là những dòng code cơ bản để có thể tạo constraint cho 1 view bằng code. Và nó khá dễ đọc và dễ hiểu. Dòng cuối cùng của đoạn code trên nhằm mục đích active một chuỗi các constraint mà bạn tạo ra.
Kết quả chúng ta thu được sẽ như hình dưới đây:
UIView cung cấp cho chúng ta 1 tập hợp các thuộc tính neo cho phép bạn thiết lập các mối quan hệ giữa các View với nhau. Dưới đây là danh sách các thuộc tính neo:
extension UIView {
/* Constraint creation conveniences. See NSLayoutAnchor.h for details.
*/
@available(iOS 9.0, *)
open var leadingAnchor: NSLayoutXAxisAnchor { get }
@available(iOS 9.0, *)
open var trailingAnchor: NSLayoutXAxisAnchor { get }
@available(iOS 9.0, *)
open var leftAnchor: NSLayoutXAxisAnchor { get }
@available(iOS 9.0, *)
open var rightAnchor: NSLayoutXAxisAnchor { get }
@available(iOS 9.0, *)
open var topAnchor: NSLayoutYAxisAnchor { get }
@available(iOS 9.0, *)
open var bottomAnchor: NSLayoutYAxisAnchor { get }
@available(iOS 9.0, *)
open var widthAnchor: NSLayoutDimension { get }
@available(iOS 9.0, *)
open var heightAnchor: NSLayoutDimension { get }
@available(iOS 9.0, *)
open var centerXAnchor: NSLayoutXAxisAnchor { get }
@available(iOS 9.0, *)
open var centerYAnchor: NSLayoutYAxisAnchor { get }
@available(iOS 9.0, *)
open var firstBaselineAnchor: NSLayoutYAxisAnchor { get }
@available(iOS 9.0, *)
open var lastBaselineAnchor: NSLayoutYAxisAnchor { get }
}
Với mỗi Anchor nó trả về các subclass từ NSLayoutAnchor đi kèm với một số phương thức phổ biến để thiết lập mối quan hệ. Nó bao gồm =, >, <, >=, <=. Cũng giống như khi chúng ta dùng Interface Builder. Đây là tài liệu để làm quen về nó Documents.
NOTE: Như các bạn đã thấy, nó chỉ hỗ trợ từ iOS 9, Hầu hết các ứng dụng bây giờ đều sẽ hỗ trợ từ iOS 9 trở lên, vì vậy chúng ta không cần phải lo lắng quá nhiều về nó.
Order of constraints
Khi các bạn bắt đầu viết các constraint của mình, điều quan trọng là bạn phải nhớ thứ tự của các constraint của bạn khi bạn làm việc với các constants.
Mục đích của đoạn code này là tạo ra mảng constraint cho innerView sao cho top của nó bằng top của của outerView, bot = bot của outerView, và cách đều 2 bên = 40 points. let constraints = [ innerView.topAnchor.constraint(equalTo: outerView.topAnchor), innerView.leftAnchor.constraint(equalTo: outerView.leftAnchor, constant: 40), innerView.bottomAnchor.constraint(equalTo: outerView.bottomAnchor), innerView.rightAnchor.constraint(equalTo: outerView.rightAnchor, constant: -40) ] Ở dòng được highlight kia giá trị của constant phải = -40. Vì lúc này vị trí của outerView đã được cố định, và outerView.rightAnchor lúc này được coi là gốc(Ox) của trục tọa độ. Vậy nên vị trí bên trái của gốc tọa độ Ox ta hiểu là giá trị âm và ngược lại. Ta có thể đổi ngược lại vị trí của innerView và outerView để được kết quả như trên hình theo đoạn có dưới đây:( Không nên viết theo kiểu này mà nên viết theo kiểu trên để dễ nhìn hơn) outerView.rightAnchor.constraint(equalTo: innerView.rightAnchor, constant: 40)
Tương tự đối với top và bottom là trục Oy hướng xuống dưới, phía trên của gốc Oy là giá trị âm và ngược lại.
NOTE: Khi các bạn thêm các constraint bằng code, các bạn nên làm theo thứ tự để code của ta dễ kiểm soát và dễ hiểu hơn.
Một số Layout guides hay sử dụng
UIVew cũng có một vài Layout guides có thể được sử dụng làm neo như sau:
layoutMarginGuide: Đặt các constraint và giữ lề của layout bằng 1 khoảng trống cố định (16 points).
readableContentGuide: Constraints chiều rộng của view giúp người dùng dễ đọc.
safeAreaLayoutGuide: Đặt các constraint giúp view của bạn không bị che bởi các thanh và nội dung khác.
Đây là đoạn code mẫu sử dụng Layout guide(safeAreaLayoutGuide)
Có vẻ rất rõ ràng khi bạn sử dụng leftAnchor và rightAnchor, nhưng bạn sẽ phải nghĩ về việc sử dụng leadingAnchor và trailingAnchor để thay thế. Nhằm mục đích hỗ trợ các ngôn ngữ từ phải qua trái. Điều này rất quan trọng khi sử dụng views như là Labels trong trường hợp bạn muốn chúng được lật lại cho các ngôn ngữ từ phải sang trái.
Mình hi vọng bài viết này giúp các bạn có thể tạo các constraints bằng code sẽ dễ dàng hơn. Nó sẽ là một giải pháp thay thế tuyệt vời để thiết lâp các constraints khi bạn không muốn dùng Interface Builder.
Như các bạn đã biết, để giỏi một kĩ năng nào đó trước hết chúng ta phải biết nó là gì và cách sử dụng của nó như thế nào. Và luyện tập nó nhiều sẽ giúp các bạn nâng cao kỹ năng đó. Bài viết này mình sẽ giới thiệu tới các bạn cách tương tác với Interface Builder và sử dụng chúng trong Auto layout sao cho hiệu quả. Hi vọng nó sẽ giúp ích cho mọi người :v
Interface builder là một phần trong ứng dụng XCode, nó giúp các nhà phát triển phần mềm iOS xây dựng giao diện trên đó.
2. Tương tác với Interface Builder
Dưới đây là hình ảnh về giao diện của nó
1.2 View as …
Ở thanh điều khiển này bạn có thể xem giao diện khác nhau như
• Các thiết bị khác nhau từ kích thước lớn nhất tới nhỏ nhât, từ iphone đến iPad • Các Interface Style khác nhau như Dark mode, light mode • Các orientation(Chiều xem màn hình) khác nhau Cái này để kiểm tra trước xem việc auto layout của các bạn đã theo ý muốn hay chưa.
1.3 Auto layout bar
Update Frames: Nút này sẽ được hiển thị khi mà một hoặc nhiều View đang không nằm đúng vị trí so với constraints nó đang có. Khi bấm vào chúng ta sẽ được giúp các view đó trở về đúng vị trí so với constraints của nó đang có.
Align: Để tạo các constraint căn chỉnh giữa các view, chẳng hạn như căn giữa, trái, phải, trên dưới …
Add new constraints: Để tạo các các constraint thông thường như trên, dưới, trái, phải, chiều rộng, chiều cao và tỉ lệ (aspect ratio)
Resolve AutoAuto Layout Issues: Để giải quyết các vấn đề về auto layout như: – Update constraint constants: Để cập nhật giá trị mới cho constraint đó theo đúng vị trí mà nó đang nằm trên SupperView. – Add missing Constraints: tự động thêm các constraint còn thiếu (Không nên dùng vì rất dễ dẫn đến việc thêm các constraint không mong muốn. – Clear constraint: Bỏ hết constraint
Embed In: Tạo 1 view mới và đưa hết các view được chọn vào trong đó. Mà có thể sẽ không mất đi các constraint hiện tại của các view được chọn.
1.3.1 Align
Add new alignment constraints
Ở đây chúng ta thấy giao diện thêm các constraints về căn chỉnh được hiển thị
Vertical in Container và Horizontally in container
Vertically in container: giúp tạo constraint của các View được chọn gắn với super view. Trong đó các connstraint được tạo sẽ giúp các view đó căn giữa theo chiều dọc với super view của nó. Ở đây chúng ta có thể thay đổi constant của nó(mặc định là 0)
Horizontally in container: Tương tự như trên nhưng theo chiều ngang.
Kết quả khi add 2 contraint này cho button
First Baselines
Khác với 2 thằng ở trên, các thằng còn lại cần phải có 1 thằng “A” đã xác đinh được vị trí của nó(đầy đủ contraint). Thằng “A” này làm nhiệm vụ là cái mỏ neo giúp cho các thằng khác bám vào.
Thằng này dùng để làm cho nhiều View( UILabel, UIButton …) có text nằm trên cùng hàng, không bị lệch lẹo. Để enable tính năng này lên chúng ta cần phải chọn ýt nhất 2 Views.
NOTE: Nó căn theo đáy của text trong các views
Kết quả
Vertical center
Tạo constraints giúp các views căn giữa theo chiều dọc Lưu ý: Nó căn theo frame size nên nó sẽ căn theo các frame của các views được thêm constraint
Để hiểu rõ hơn chúng ta theo dõi hình dưới đây
Horizontal center
Tương tự như Vertical center nhưng theo chiều ngang.
Bottom edges
Dùng khi cần gắn constraint cho viewA luôn nằm ở đáy của viewB. Mặc định sẽ là viewA.bottom to viewB.bottom
Top edges
Dùng khi cần gắn constraint cho viewA nằm ở trên đầu của viewB. Mặc định sẽ là viewA.top to viewB.top
Trailing edges
Dùng khi cần gắn contraint cho viewA cùng căn trái 1 khoảng bằng căn trái của viewB Măc định sẽ là viewA.trailing to viewB.trailing
Leading edges
Dùng khi cần gắn constraints cho viewA cùng căn phải 1 khoảng bằng với căn phải của viewB. Mặc định sẽ là viewA.leading to viewB.leading
Tất cả các Label được constraint xoay quanh trụ là Button.
1.3.2 Add new constraints
Thường dùng để thêm các constraints cho view được chọn như: trên, dưới, trái, phải, chiều rộng, chiều cao, tỉ lệ. Phần này thì mọi người dùng nhiều rồi nên mình không nói nhiều về nó. NOTE: Thông thường khi chọn XCode sẽ gợi ý gắn constraint với view gần với các cạnh của nó nhất(không tính view đè lên nhau) Tuy nhiên chúng ta có thể thay đổi constraint tới view đích bằng cách chọn mũi tên như hình dưới.
Ở hình này bạn có thể thay đổi view đích
width: tạo constraint độ rộng của view
height: tạo constraint độ cao của view
equal width: Tạo constraints cho các view có cùng độ rộng (Chọn 2 view trở lên)
equal height: Tạo constraints cho các view có cùng độ cao (chọn 2 view trở lên)
Aspect ratio: tạo constraint tỉ lệ. Thường sử dụng cho những view sử dụng hình ảnh.
NOTE: Bạn có thế chọn nhiều view rồi set equal width, height để set cho tất cả các view được chọn có cùng size.
1.3.3 Embed In
Embed In View Dùng khi bạn muốn gom các view được chọn vào 1 view lớn, để quản lý layout đễ hơn. Tính năng này sẽ tự tạo 1 một view lớn chứa và cách các view bên trong với inset cố định, mặc định là 20 points
Embed In View Without Inset Khác với tính năng Embed In View tính năng này sẽ tạo với Inset = 0, cách dùng như Embed In View
Embed In ScrollView Tự động tạo 1 scrollview và đưa tất cả các view được chọn vào bên trong nó.
Embed In Stack View Tạo ra 1 stack view và đưa các view được chọn vào trong nó. Thằng này thường được dùng khi bạn có nhiều View có cùng kích thước. Mình sẽ viết 1 bài đi sâu vào thằng này sau.
1.4 Add constraints bằng Chuột và bàn phím.
Bạn có thể add constraint bằng cách: – Sử dụng chuột phải để kéo ViewA constraint với ViewB. – Bấm giữ phím control + chuột trái để kéo ViewA constraint với ViewB Khi đó 1 popup sẽ hiển thị cho chúng ta thêm Constraint một cách nhanh chóng.
Lúc này bạn có thể chọn 1 constraint mà bạn muốn hoặc giữ Shift để chọn tạo nhiều constraint. Giữ phím Option để nhìn thấy chỉ số và tạo constraint với chỉ số đó.
1.5 Thanh Menu
Đối với mỗi người sẽ có sở thích khác nhau khi chọn hiển thị cái gì trên màn hình Interface Builder. Nhưng theo mình các bạn nên chọn hiển thị các mục như mình đã chọn :D, đặc biệt có mục Bounds Rectangles: Nó tạo ra các border xung quanh các View giúp các bạn dễ hình dung hơn khi thêm constraints.
Preview mode
Giúp các bạn có thể nhìn thấy Layout của mình hoạt động như nào mà không cần phải build app. Tính năng này Cực Kỳ hữu dụng khi các bạn muốn test layout. Nó còn tiết kiệm thời gian khi bạn đang làm trong 1 dự án lớn vì thời gian build app nó khá lâu.
Ở chế độ preview bạn có thế bấm vào Layout để chỉnh lại bố cục của chế độ xem trước Hiện tại có 2 lựa chọn: Bên phải và xem ở dưới.
Assistant
Tính năng này chia đôi màn hình và tự động hiển thị file swift đang được kết nối với nó. Để chuyển qua tính năng này chúng ta mở thanh menu lên và chọn Assistant Hoặc dùng tổ hợp phím Option + Control + Command + Enter để mở nhanh.
1.6 Xem và thay đổi các constraint trên 1 view
Tab Show the size inspector
Bạn có thể chọn 1 view và xem được tất cả các constraint liên quan tới nó ở tab Show The Size Inspector bên trên góc trái màn hình. Nếu muốn chỉnh sửa nhanh constant, priority và multiplier của constraint đó thì hãy bấm vào nút edit ở mỗi constraint.
Nếu muốn xem và chỉnh sửa 1 constraint cụ thể thì các bạn có thể dùng chuột chon constraint đó và sửa nó. Ở Mục này ta có thể sửa được nhiều thông tin hơn, ta có thể thay đổi được constraint với 1 vị trí khác. Thậm chí cả 1 view khác luôn. Cụ thể xem dưới hình.
Để có thể tạo ra các ứng dụng iOS thì việc sử dụng auto layout là không thể tránh khỏi. Đối với các Developer đã có kinh nghiệm thì có vẻ không khó khăn là bao. Nhưng đối với các iOS Developer mới họ thường khá lúng túng với công việc này. Vì vậy loạt bài viết lần này mình sẽ chia sẻ về Auto layout để các bạn có thể cảm thấy tự tin hơn khi sử dụng Auto Layout 😀
Để sử dụng Auto Layout thì đầu tiên chúng ta phải hiểu nó là cái gì và sử dụng để làm gì. Vì vậy phần 1 mình sẽ nói về định nghĩa của Auto layout cho các bạn mới làm về iOS giúp các bạn hiều và dần làm quen với nó. OK, Chúng ta bắt đầu thôi :v
Auto layout là gì?
Auto layout là việc tự động tính toán kích thước và vị trí của tất cả các “view” trong view hierarchy của bạn, dựa trên các ràng buộc được đặt trên các “view” đó.
Ví dụ: Bạn có thể giới hạn một Button sao cho Button nằm ở giữa theo chiều ngang với ImageView và sao cho cạnh trên của Button luôn cách 8 “points” so với cạnh dưới ImageView. Lúc này, nếu kích thước hoặc vị trí của ImageView thay đổi thì vị trí của Button sẽ tự động điều chỉnh cho phù hơp. (Hình 1)
Cách tiếp cận dựa trên ràng buộc này để thiết kế cho phép bạn xây dựng giao diện người dùng tự động đáp ứng với cả những thay đổi bởi các tác nhân bên trong và bên ngoài(Internal and external changes).
Hình 1
NOTE: • “view”: Ở đây được hiểu là tập hợp các UI để hiển thị trên màn hình như: UILabel, UIButton, UIView .v.v. • “points”: Là đơn vị đo lường sử dụng trong thiết kế UI của iOS.
Những tác nhân thay đổi view từ bên ngoài (External Changes)
Nó xảy ra khi kích thước hoặc hình dạng của supperview thay đổi. Với mỗi thay đổi, bạn sẽ phải cập nhật lại layout của view hierarchy để sử dụng không gian có sẵn một cách tốt nhất. Dưới đây là một số tác nhân thay đổi bên ngoài phổ biến: • Người dùng thay đổi kích thước cửa sổ (window) trong OS X. • Người dùng truy cập hoặc rời khỏi Split View trên iPad. • Người dùng sử dụng tính năng xoay màn hình (Rotates) (iOS). • Khi các thanh cuộc gọi hoặc ghi âm xuất hiện hoặc biến mất (iOS). • Khi bạn muốn hỗ trợ các size class khác nhau. • Khi bạn muốn hỗ trợ các kích thước màn hình khác nhau
Hầy hết những tác nhân thay đổi này có thể xảy ra trong thời gian chạy của ứng dụng và chúng yêu cầu phản hồi linh hoạt từ ứng dụng của bạn. Ngoài ra, như là việc hỗ trợ cho các màn hình với kích thước khác nhau, ứng dụng cũng cần thích nghi với các môi trường khác nhau. Mặc dù kích thước màn hình thường không thay đổi lúc runtime, nhưng việc tạo một giao diện người dùng dễ dàng thích thích nghi(adaptive) khiến cho ứng dụng của các bạn chạy tốt trên cả iPhone 4S, iPhone 6 Plus, iPhone X thậm chí là cả iPad. Auto Layout cũng là phần chủ đạo cho việc hỗ trợ Slide Over và Split view trên iPad.
Những tác nhân thay đổi view từ bên trong (Internal Changes)
Nó xảy ra khi kích thước của các view hoặc các controls(Thanh điều khiển) trong giao diện người dùng của bạn thay đổi.
Dưới đây là một vài nguyên nhân phổ biến:
Nội dung bị thay đổi bởi những thay đổi từ ứng dụng.
Ứng dụng hỗ trợ đa quốc gia
Ứng dụng hỗ trợ Dynamic type (iOS): Cho phép thay đổi style của ứng dụng (font, fontsize, …)
Khi mà mội dung của ứng dụng thay đổi, nội dung mới có thể yêu cầu một layout khác với layout hiện tại. Điều này thường xảy ra trong ứng dụng khi nó hiển thị text hoặc image(hình ảnh). Ví dụ, một ứng dụng tin tức cần phải điều chỉnh layout của nó dựa trên kích thước của từng loại tin tức. Tương tự, một ứng dụng ghép ảnh phải xử lí một loạt các kích thước của hình ảnh và tỉ lệ khung hình(aspect ratio).
Việc làm ứng dụng hỗ trợ đa quốc gia là quá trình tạo ra một ứng dụng có thể hiển thị tốt ở trên các ngôn ngữ, khu vực và văn hóa khác nhau. Bố cục của ứng dụng hỗ trợ đa quốc gia phải tính đến những khác biệt này và cần hiển thị chính xác trong tất cả các ngôn ngữ và khu vực mà ứng dụng hỗ trợ.
Ứng dụng hỗ trợ đa quốc gia có 3 tác dụng chính trên bố cục. Đầu tiên khi bạn chuyển giao diện sang một ngôn ngữ khác thì các Label sẽ yêu cầu phải có một khoảng trống khác. Ví dụ như: Tiếng đức thì cần nhiều không gian hơn tiếng anh. Tiếng nhật thì lại cần ít khoảng trống hơn nhiều.
Thứ hai, Đinh dạng bạn sử dụng để hiển thị ngày và số có thể thay đổi giữa các khu vực, ngay cả khi ngôn ngữ không bị thay đổi. Mặc dù những thay đổi này thường không to bằng thay đổi ngôn ngữ, nhưng giao diện người dùng vẫn cần thích ứng với những thay đổi đó.
Thứ ba, thay đổi ngôn ngữ có thể ảnh hưởng không chỉ đến kích thước của văn bản, mà cả tổ chức bố cục của nó. Các ngôn ngữ khác nhau sẽ sử dụng các hướng bố trí khác nhau. Ví dụ như: Tiếng Anh thì bố trí từ trái qua phải, còn tiếng Ả Rập và Do Thái thì ngược lại. Nói chung, thứ tự của các phần trong giao diện người dùng phải phù hợp với hướng bố trí bố cục trong ngôn ngữ đó. Nếu một Button nằm ở góc dưới bên phải của màn hình khi sử dụng Tiếng Anh thì nó nên được được nằm ở góc dưới bên trái khi sử dụng tiếng Ả Rập.
Cuối cùng, nếu ứng dụng iOS của bạn hỗ trợ kiểu Dynamic type, người dùng có thể thay đổi kích thước, phông chữ được sử dụng trong ứng dụng của bạn. Điều này có thể thay đổi cả chiều cao lẫn chiều rộng của bất kỳ text trong giao diện người dùng của bạn. Nếu người dùng thay đổi kích thước phông chữ(font) của bạn khi ứng dụng đang chạy thì cả phông chữ (font) và bố cục(layout) đều phải thích ứng với thay đổi đó.
Tổng kết
Auto Layout là việc định nghĩa tập hợp các ràng buộc. Các ràng buộc này đại diện cho quan hệ giữa các view với nhau. Auto layout sau đó sẽ tính toán kích thước và vị trí của các view dựa trên các ràng buộc của chúng. Điều này tạo ra các bố cục tự đáp ứng được cả với thay đổi bên trong và bên ngoài. Để sử dụng auto layout chúng ta cần phải hiểu về logic đằng sau các layouts dựa trên các ràng buộc của nó.