View Debugging in Xcode

Khi bạn lập trình một ứng dụng, nhiều khi có bug trên đối tượng view của bạn hay những bug về layout constraints (những thứ mà không dễ để bạn có thể tìm thấy chỉ bằng cách đọc code của bạn)

Vậy khi bạn trong tình huống như vậy bạn sẽ làm thế nào? Trong bài hôm nay mình sẽ giới thiệu cho các bạn một kĩ thuật gọi là view debugging.

Thay vì bạn printing frames của view ra console và cố gắng tưởng tượng nó trong đầu thì giờ đây bạn cực kì đơn giản để có thể khắc phục được vấn đề về layout constraint hoặc không còn phải tưởng tượng nữa.

Bắt đầu

  • Trong bài này, tôi sẽ sử dụng một thư viện là JSQMessagesViewController được viết bởi Jesse Squires. UI của thư viện này trong rất thân thiện và nó có vẻ như là một app message. Bạn có thể lấy mã nguồn trên github theo đường dẫn GitHub

  • Bạn tap vào Push via Storyboard, bạn sẽ thấy cuộc nói chuyện giữa Steve JobsTim Cook.

Screen-Shot-2015-04-07-at-12.03.28-PM-280x500.png

  • Đó là hình ảnh về cuộc nói chuyện đó. Tiếp theo bạn quay trờ lại với Xcode và click vào Debug View Hierachy trên thanh Debug Bar. Ngay lập tức bạn sẽ được Xcode đưa tới Debug\View Debugging\Capture View Hierarchy.

Screenshot-1.png

  • Lúc này Xcode sẽ làm gián đoạn ứng dụng của bạn và trao quyền cho trình gỡ lỗi. Bạn có thể hiểu đơn giản là ứng dụng của bạn đang tạm dừng tại thời điểm mà bạn nhấn vào debug UI button.

  • Ngoài ra Xcode thay thế trình biên dịch mã với các khung hình (frames) trong toàn bộ hệ thống phân cấp view tại thời điểm gián đoạn. Nó bao gồm cả các line chỉ ra ranh giới giữ các điểm, các view khác nhau.

Screenshot-2-315x500.png

  • Như bạn đã biết khi bạn add subview một view nào đó, bạn sẽ add nó lên layer top của stack views. Bởi vì phần lớn các view không overlap và mỗi view sẽ là một phần của view cha (parent view). Với hình ảnh trên bạn sẽ quan sát thấy là một loạt các line được thêm vào bao quanh một đối tượng nào đó (như UIButton, UILabel, UIImage)

Screenshot-3.gif

  • Bạn hãy quan sát hình ảnh mô tả chính là hệ thống phân cấp các view. Rất dễ dàng để bạn thấy các đặc trưng như side, top, bottom, corner… Nó thật tuyệt phải không?

  • Nếu chỉ thế thì nó chưa phải sức mạnh của Xcode đối với chức năng này. Vậy sử dụng thế nào là hữu ích? Ngay bây giờ bạn sẽ nhìn thấy một stack view từ layer top tới layer bottom (bạn cứ hình dùng như các layer đang chồng lên nhau, mỗi layer có thể là nhiều view). Ban đầu bạn sẽ thấy là một giao diện phẳng 2D nhưng khi bạn nhấp chuột và move thì bạn sẽ thấy một giao diện mô phỏng 3D (rất trực quan) -> đó chính là phân cấp tầm nhìn của bạn. Các lớp chồng lên nhau, nối tiếp nhau và bạn sẽ cảm thấy như theo thứ tự bạn add subview.

Khám phá hệ thống các view

  • Điều đặc trưng và phố biến trong mô hình 3D là bạn sẽ có cái nhìn từ phía bên trái khi bạn nhìn vào mô phỏng. Bạn hãy nhìn vào mô phỏng 3D này và thử click vào view bên trái nhất và Xcode sẽ highlights view mà bạn chọn. Bạn nhìn phía bên trên (canvas) nó sẽ update lại cho bạn đối tượng view bạn đang chọn đó là UIWindow

  • Nhìn vào hệ thống 3D này bạn sẽ thấy nó đang sử dụng một window. Tiếp theo bạn hãy thử click vào một view bên phải của view bạn vừa click. Bạn sẽ thấy trên thanh canvas thay đổi tuỳ vào từng view bạn click.

  • Trong đó một số thành phần trong hệ thống bạn sẽ thấy:

    • UINavigationTransitionView: container view của navigation controller transitions
    • UIViewControllerWrapperView: Một đối tượng wrapper view chưa view của controller
    • UIView: view trên cùng của một controller (khi bạn chưa add subview)
    • JSQMessagesCollectionView: collection view được sử dụng trong project để hiển thị tất cả các tin nhắn

Tập trung vào view bạn cần

Screen-Shot-2015-04-07-at-12.24.14-PM-425x500.png

  • Trong trường hợp gỡ lỗi UI của hệ thống 3D này, ban đầu bạn sẽ thấy rất nhiều view và cảm thấy rắc rối. Vậy bạn cần phải biết view nào bạn cần debug và làm thế nào để loại bỏ các view không liên quan.

  • Để làm điều đó bạn có thể sử dụng thanh trượt màu đỏ bao quanh như hình vẽ. Bãn hãy thử kéo và sẽ thấy các cửa sổ, các view và UINavigationTransitionView sẽ dần biến mất.

  • Vậy trong trường hợp các view nhỏ và các view sát nhau, bạn khó chọn, khó debug, làm thế nào để bạn có thể kéo dãn khoảng cách các view ra xa nhau và phóng to lên?

Screen-Shot-2015-03-15-at-5.03.26-PM-700x136.png

  • Trên hình là các nút phóng to, thu nhỏ, reset tỉ lên zoom in và zoom out. Thanh trượt bên trái giúp giãn cách các view

Lựa chọn khác cho view

  • Làm thế nào để bạn có thể cho hệ thống 3D quay trở lại ban đầu? Rất đơn giản

Screen-Shot-2015-03-15-at-5.03.26-PM-copy-700x136.png

  • Biểu tượng được đánh dấu màu đỏ trong hình.

  • Khi bạn nhận thấy rằng những gì bạn đang nhìn thấy không đủ thông tin, không chinhs xác. Bạn có thể sử dụng wireframe cho view. Bạn có thể tắt chức năng này bằng nut View Mode.

Kiểm tra View

  • Làm thế nào để có thể dễ dàng truy cập tới các view. Bạn có thể nhấn cmd + 6. Kết quả giống như hình vẽ:

Screenshot-12-310x500.png

  • Để thực sự hiểu hơn về các vị trí của view bạn có thể xem tại Inspector (cmd + 4). Các thông tin về vị trí và một số thông tin khác có thể bạn cần được hiển thị bên phải.

Screenshot-13-700x255.png

  • Nó vẫn được tính toán để hiển thị khi bạn sử dụng autolayout

Screenshot-14-225x500.png

Extract thông tin view ra màn hình console

  • Có hai cách để bạn có thể trích xuất thông tin view hoặc thông tin của một commane nào đó.
    • Cách thứ nhất bạn chuột phải vào view đang cần thông tin và chọn print.
    • Cách thứ hai khi muốn xem kết quả một mã code nào đó bạn sử dụng lệnh po command
po [self.contentView recursiveDescription]

Và kết quả:

<UIView: 0x7fde6c475de0; frame = (0 0; 312 170); gestureRecognizers = <NSArray: 0x7fde6c484fe0>; layer = <CALayer: 0x7fde6c474750>>
   | <JSQMessagesLabel: 0x7fde6c475eb0; baseClass = UILabel; frame = (0 0; 312 20); text = 'Today 10:58 PM'; clipsToBounds = YES; opaque = NO; autoresize = RM+BM; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x7fde6c476030>>
   | <JSQMessagesLabel: 0x7fde6c476400; baseClass = UILabel; frame = (0 20; 312 0); clipsToBounds = YES; opaque = NO; autoresize = RM+BM; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x7fde6c476580>>
   | <UIView: 0x7fde6c476b50; frame = (70 20; 210 150); autoresize = RM+BM; layer = <CALayer: 0x7fde6c474dd0>>
   |    | <UIImageView: 0x7fde6c482880; frame = (0 0; 210 150); opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x7fde6c476ae0>> - (null)
   | <UIView: 0x7fde6c482da0; frame = (282 140; 30 30); autoresize = RM+BM; layer = <CALayer: 0x7fde6c482d00>>
   |    | <UIImageView: 0x7fde6c482e70; frame = (0 0; 30 30); opaque = NO; autoresize = RM+BM; userInteractionEnabled = NO; layer = <CALayer: 0x7fde6c482f70>> - (null)
   | <JSQMessagesLabel: 0x7fde6c483390; baseClass = UILabel; frame = (0 170; 312 0); clipsToBounds = YES; opaque = NO; autoresize = RM+BM; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x7fde6c483510>>
  • Vậy là một số kĩ thuật trong bài nhằm mục đích debug UI giúp bạn nhanh chóng giải quyết vấn đề một cách nhanh hơn. Cám ơn các bạn đã đọc bài viết.

  • Một số hình ảnh được lấy từ raywenderlich

Kết luận: Các bạn hãy cố gắng debug UI bằng cách này, sẽ rất hữu ích và tiết kiệm thời gian đó.

All Rights Reserved