Tôi đã fix bug layout của một app iOS như thế nào?

28 May 2019 — Written by Chính Phạm
#iOS#swift#thu thuat iOS

Là một Đì Vê Lốp Pơ iOS chắc hẳn các bạn đã quá quen thuộc với thông báo bên dưới mỗi khi build app đúng không? Hoặc có thể bạn thấy rồi nhưng "Bố éo quan tâm, app chạy mà không crash là được rồi!"

Chuẩn bệnh

2019-05-28 21:54:19.487436+0700 BadApp[64515:4639641] [LayoutConstraints] Unable to simultaneously satisfy constraints.
	Probably at least one of the constraints in the following list is one you don't want. 
	Try this: 
		(1) look at each constraint and try to figure out which you don't expect; 
		(2) find the code that added the unwanted constraint or constraints and fix it. 
(
    "<NSLayoutConstraint:0x600002ca9590 UILabel:0x7f972be044e0'Label'.height == 50   (active)>",
    "<NSLayoutConstraint:0x600002ca76b0 UILabel:0x7f972be044e0'Label'.firstBaseline == UIStackView:0x7f972be08110.firstBaseline   (active)>",
    "<NSLayoutConstraint:0x600002ca7b10 UIButtonLabel:0x7f972be146d0.centerY == UIButton:0x7f972bc06ec0'Button'.centerY   (active)>",
    "<NSLayoutConstraint:0x600002ca7c50 UIButtonLabel:0x7f972be146d0.height <= UIButton:0x7f972bc06ec0'Button'.height   (active)>",
    "<NSLayoutConstraint:0x600002ca78e0 'UISV-canvas-connection' UIStackView:0x7f972be13440.top == UIButton:0x7f972bc06ec0'Button'.top   (active)>",
    "<NSLayoutConstraint:0x600002c9cc30 'UISV-canvas-connection' UIStackView:0x7f972be08110.top == UIStackView:0x7f972be13440.top   (active)>",
    "<NSLayoutConstraint:0x600002c9ed50 'UISV-spacing' V:[UILabel:0x7f972be044e0'Label']-(0)-[UIStackView:0x7f972be08110]   (active)>"
)

Will attempt to recover by breaking constraint 
<NSLayoutConstraint:0x600002ca7b10 UIButtonLabel:0x7f972be146d0.centerY == UIButton:0x7f972bc06ec0'Button'.centerY   (active)>

Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKitCore/UIView.h> may also be helpful.

Tuy là nó cảnh báo như vậy, nhưng app vẫn chạy được, không vấn đề gì xảy ra... cho đến khi bạn hoặc khách hàng muốn update UI chổ đó lại, ví dụ thêm một cái button, label, image, view... vân vân và mây mây 🙈

Ôi thôi, update đã đời rồi đột nhiên cái button nó lại văng đâu mất tiêu, trong khi trên file *xib vẫn còn sờ sờ đó 😝

Demo lỗi layout iOS

Mình dựng tạm cái demo lỗi layout cho các bạn dễ hình dung:

  • Khung màu đỏ: là nơi preview UI của xCode
  • Khung màu xanh: Máy ảo để chạy app
  • Khung màu vàng: Đây là nơi log ra lỗi.

Nhìn chổ preview thì có vẻ yên tâm lắm, layout chuẩn như này chắc ổn rồi, run phát xem thử nà :v Nói chung là khó mà phát hiện được bug do view hoặc phần tử nào nào gây ra lắm.

Giải quyết vấn đề

Ok, sau khi chuẩn bệnh xong thì ta cùng tìm cách giải quyết vấn đề này thôi!

Đầu tiên, phải kiếm một đứa thông dịch cái chổ log lỗi đó xem nó mô tả là bị gì đã nhé.

Các bạn vào web này: 🚀 WTF Auto Layout?, copy từ dấu (...)

Ví dụ:

(
    "<NSLayoutConstraint:0x600002ca9590 UILabel:0x7f972be044e0'Label'.height == 50   (active)>",
    "<NSLayoutConstraint:0x600002ca76b0 UILabel:0x7f972be044e0'Label'.firstBaseline == UIStackView:0x7f972be08110.firstBaseline   (active)>",
    "<NSLayoutConstraint:0x600002ca7b10 UIButtonLabel:0x7f972be146d0.centerY == UIButton:0x7f972bc06ec0'Button'.centerY   (active)>",
    "<NSLayoutConstraint:0x600002ca7c50 UIButtonLabel:0x7f972be146d0.height <= UIButton:0x7f972bc06ec0'Button'.height   (active)>",
    "<NSLayoutConstraint:0x600002ca78e0 'UISV-canvas-connection' UIStackView:0x7f972be13440.top == UIButton:0x7f972bc06ec0'Button'.top   (active)>",
    "<NSLayoutConstraint:0x600002c9cc30 'UISV-canvas-connection' UIStackView:0x7f972be08110.top == UIStackView:0x7f972be13440.top   (active)>",
    "<NSLayoutConstraint:0x600002c9ed50 'UISV-spacing' V:[UILabel:0x7f972be044e0'Label']-(0)-[UIStackView:0x7f972be08110]   (active)>"
)

Demo WTF Auto Layout?

Sau khi copy & paste thì bạn nhận được kết quả tương tự như hình trên, nó mô tả về một số nguyên nhân gây ra conflict giữa các view với nhau.

Một số bạn cẩn thận hơn là đổi tên các view cho dễ nhìn thì nó sẽ hiển thị lên cho ta thấy luôn, tuy nhiên do mình lười đổi tên từng view quá, vì thế mỗi lần bug là nhức đầu 😂

Bắt đầu thôi!!!

Bấm vào nút Debug View Hierarchy 😎

Debug View Hierarchy

Nhập đoạn sau chổ (lldb) để xCode set màu cho các view bị lỗi, như vậy sẻ dễ dàng hơn trong công tác điều tra 👮‍♂️

(lldb) ex [(UIView *)0x7f972bc06ec0 setBackgroundColor: [UIColor greenColor]]
(lldb) ex [(UILabel *)0x7f972be044e0 setBackgroundColor: [UIColor redColor]]
...

Phân tích cú pháp trên một chút:

ex [(TÊN_CỦA_VIEW *)ID_CỦA_VIEW setBackgroundColor: [UIColor TÊN_MÀU]]
  • TÊN_CỦA_VIEW: chổ này bạn có thể hiểu nó là một view, button, label...etc
  • ID_CỦA_VIEW: <NSLayoutConstraint:0x600002ca9590 UILabel:0x7f972be044e0'Label'.height == 50 (active)> phần mình bôi đậm chính là ID của đối tượng cần set background.
  • TÊN_MÀU: Chổ này có thể dùng greenColor, blueColor, yellowColor...etc

Continue Program Execution

Sau khi set màu cho các view xong thì các bạn nhấn vào nút Continue Program Execution và nhấn nút Debug View Hierarchy thêm một lần nữa để thấy kết quả nhé!

Kết quả debug UI

Như bạn đã thấy rồi đó, việc còn lại là của bạn 😋, túm đầu đứa nào gây ra bug mà trị thôi 😂

À cách này thực sự có ích cho các bạn dev app của Nhật, vì một số label hoặc view có kí tự tiếng Nhật, lúc debug UI thì ôi thôi không buồn nói 😂 toàn kí tự encode.

Cảm ơn bạn đã dành thời gian đọc bài viết ❤️