setNeedLayout so với setNeedUpdateConstraint và layout IfNeeded vs updateConstraint IfNeeded


227

Tôi biết rằng về cơ bản chuỗi bố cục bao gồm 3 quá trình khác nhau.

  1. hạn chế cập nhật
  2. chế độ xem bố cục (đây là nơi chúng tôi nhận được tính toán của khung)
  3. trưng bày

Điều không hoàn toàn rõ ràng với tôi là sự khác biệt bên trong giữa -setNeedsLayout-setNeedsUpdateConstraints. Từ Apple Docs:

setNeedLayout

Gọi phương thức này trên luồng chính của ứng dụng của bạn khi bạn muốn điều chỉnh bố cục của các khung nhìn của khung nhìn. Phương pháp này làm cho một ghi chú của yêu cầu và trả lại ngay lập tức. Bởi vì phương pháp này không bắt buộc cập nhật ngay lập tức, nhưng thay vào đó, chờ đợi chu kỳ cập nhật tiếp theo, bạn có thể sử dụng nó để vô hiệu hóa bố cục của nhiều chế độ xem trước khi bất kỳ chế độ xem nào được cập nhật. Hành vi này cho phép bạn hợp nhất tất cả các cập nhật bố cục của bạn thành một chu kỳ cập nhật, thường tốt hơn cho hiệu suất.

setNeedUpdateConstraint

Khi một thuộc tính của chế độ xem tùy chỉnh của bạn thay đổi theo cách có thể tác động đến các ràng buộc, bạn có thể gọi phương thức này để chỉ ra rằng các ràng buộc cần được cập nhật tại một thời điểm nào đó trong tương lai. Sau đó, hệ thống sẽ gọi updateConstraint như một phần của bố cục thông thường. Cập nhật tất cả các ràng buộc cùng một lúc ngay trước khi chúng cần thiết đảm bảo rằng bạn không cần tính toán lại các ràng buộc khi nhiều thay đổi được thực hiện cho chế độ xem của bạn ở giữa các lần bố trí.

Khi tôi muốn tạo hiệu ứng cho một khung nhìn sau khi sửa đổi một ràng buộc và làm động các thay đổi tôi thường gọi ví dụ:

[UIView animateWithDuration:1.0f delay:0.0f usingSpringWithDamping:0.5f initialSpringVelocity:1 options:UIViewAnimationOptionCurveEaseInOut animations:^{
        [self.modifConstrView setNeedsUpdateConstraints];
        [self.modifConstrView layoutIfNeeded];
    } completion:NULL];

Tôi đã phát hiện ra rằng nếu tôi sử dụng -setNeedsLayoutthay vì -setNeedsUpdateConstraintslàm việc tất cả mọi thứ như mong đợi, nhưng nếu tôi thay đổi -layoutIfNeededvới -updateConstraintsIfNeeded, các hình ảnh động sẽ không xảy ra.
Tôi đã cố gắng đưa ra kết luận của riêng mình:

  • -updateConstraintsIfNeeded chỉ cập nhật các ràng buộc nhưng không buộc bố cục đi vào quy trình, do đó các khung ban đầu vẫn được giữ nguyên
  • -setNeedsLayoutcuộc gọi cũng -updateContraintsphương thức

Vì vậy, khi nào là ok để sử dụng một thay vì khác? và về các phương thức bố trí, tôi có cần gọi chúng trên khung nhìn có sự thay đổi trong một ràng buộc hoặc trên khung nhìn cha không?


27
Tôi không hiểu mọi người hạ thấp ... thực sự. VÌ VẬY, bạn nên làm gì đó với nó, như hỏi một lý do là bắt buộc hoặc chúng hoàn toàn vô nghĩa
Andrea

7
Có lẽ họ chỉ cần lấy huy hiệu Critic (bỏ phiếu đầu tiên)
fujianjin6471

1
Tôi rất khuyên bạn nên xem ở đây . Câu trả lời là nhiều giải pháp cho một vấn đề thực sự. Cũng xem video này
Mật ong

Câu trả lời:


258

Kết luận của bạn là đúng. Sơ đồ cơ bản là:

  • setNeedsUpdateConstraintsđảm bảo một cuộc gọi trong tương lai để updateConstraintsIfNeededcác cuộc gọi updateConstraints.
  • setNeedsLayoutđảm bảo một cuộc gọi trong tương lai để layoutIfNeededcác cuộc gọi layoutSubviews.

Khi layoutSubviewsđược gọi, nó cũng gọi updateConstraintsIfNeeded, vì vậy việc gọi nó bằng tay hiếm khi cần thiết theo kinh nghiệm của tôi. Trong thực tế, tôi chưa bao giờ gọi nó trừ khi gỡ lỗi bố cục.

Việc cập nhật các ràng buộc sử dụng setNeedsUpdateConstraintscũng khá hiếm, objc.io, bạn phải đọc về tự động thanh toán .

Nếu một cái gì đó thay đổi sau đó làm mất hiệu lực một trong những ràng buộc của bạn, bạn nên loại bỏ ràng buộc đó ngay lập tức và gọi setNeedUpdateConstraint. Trong thực tế, đó là trường hợp duy nhất mà bạn phải kích hoạt một bản cập nhật ràng buộc.

Ngoài ra, theo kinh nghiệm của tôi, tôi chưa bao giờ phải làm mất hiệu lực các ràng buộc và không đặt mã setNeedsLayouttrong dòng tiếp theo của mã, bởi vì các ràng buộc mới khá nhiều đang yêu cầu bố cục mới.

Các quy tắc của ngón tay cái là:

  • Nếu bạn thao tác trực tiếp các ràng buộc, hãy gọi setNeedsLayout.
  • Nếu bạn thay đổi một số điều kiện (như offset hoặc smth) sẽ thay đổi các ràng buộc trong updateConstraintsphương thức ghi đè của bạn (một cách được đề xuất để thay đổi các ràng buộc, btw), hãy gọi setNeedsUpdateConstraintsvà hầu hết thời gian setNeedsLayoutsau đó.
  • Nếu bạn cần bất kỳ hành động nào ở trên để có hiệu quả ngay lập tức, ví dụ như khi bạn cần tìm hiểu chiều cao khung hình mới sau khi bố trí vượt qua thì hãy gắn nó với a layoutIfNeeded.

Ngoài ra, trong mã hoạt hình của bạn, tôi tin setNeedsUpdateConstraintslà không cần thiết, vì các ràng buộc được cập nhật trước hoạt hình theo cách thủ công và hoạt ảnh chỉ hiển thị lại chế độ xem dựa trên sự khác biệt giữa cũ và mới.


@coverback, vì vậy objc.io nói "Nếu có điều gì đó thay đổi sau đó làm mất hiệu lực một trong những ràng buộc của bạn, bạn nên xóa ràng buộc đó ngay lập tức và gọi setNeedUpdateConstraint. Và sau đó trong Khối hoạt hình, nó nói rằng khi tôi xóa, thêm hoặc thay đổi ràng buộc. Tôi phải gọi setNeedLayout. Có gì khác biệt? Tôi cảm thấy thật ngu ngốc :(
pash3r

3
@ pash3r Sự khác biệt là cập nhật liên tục không đủ điều kiện là "không hợp lệ". Vô hiệu là khi nó không còn phù hợp nữa, giống như phải được gắn vào một chế độ xem khác hoặc loại bỏ hoàn toàn. Constant sẽ chỉ đặt một cái nhìn gần hơn hoặc xa hơn, hoặc thay đổi kích thước của nó, do đó cần phải có setNeedsLayout.
coverback

@coverback setNeedsLayoutđảm bảo layoutSubviewssẽ được gọi trong chu kỳ cập nhật tiếp theo, nhưng có lẽ điều này không liên quan gì layoutIfNeeded?
fujianjin6471

2
@coverback Nếu bạn thao tác trực tiếp các ràng buộc, layoutSubviewssẽ được gọi tự động, không cần gọisetNeedsLayout
fujianjin6471

Có, thao tác trực tiếp các thuộc tính của một ràng buộc sẽ kích hoạt layoutSubviews, do đó không cần phải thực hiện thủ công. Tuy nhiên, bạn phải gọi layoutIfNeedednếu bạn cần các thay đổi có hiệu lực ngay lập tức thay vì chu kỳ bố trí tiếp theo
Charlie Martin

89

Câu trả lời của coverback là khá chính xác. Tuy nhiên, tôi muốn thêm một số chi tiết.

Dưới đây là sơ đồ của chu trình UIView điển hình giải thích các hành vi khác:

Vòng đời của UIView

  1. Tôi đã phát hiện ra rằng nếu tôi sử dụng -setNeedsLayoutthay vì -setNeedsUpdateConstraintslàm việc tất cả mọi thứ như mong đợi, nhưng nếu tôi thay đổi -layoutIfNeededvới -updateConstraintsIfNeeded, các hình ảnh động sẽ không xảy ra.

updateConstraintsthường không làm gì cả. Nó chỉ giải quyết các ràng buộc mà nó không áp dụng chúng cho đến khi layoutSubviewsđược gọi. Vì vậy, hoạt hình không yêu cầu một cuộc gọi đến layoutSubviews.

  1. setNeedLayout cũng gọi phương thức -updateContraint

Không, điều này là không cần thiết. Nếu các ràng buộc của bạn chưa được sửa đổi, UIView sẽ bỏ qua cuộc gọi đến updateConstraints. Bạn cần gọi một cách rõ ràng setNeedsUpdateConstraintđể sửa đổi các ràng buộc trong quy trình.

Để gọi updateConstraintsbạn cần làm như sau:

[view setNeedsUpdateConstraints];
[view setNeedsLayout]; 
[view layoutIfNeeded];

Cảm ơn, điều này đã giải quyết vấn đề của tôi. Tôi đã có UIWindow không có UIView cha mẹ có các ràng buộc tạm thời được thêm vào nó khi gọi Layout IfNeeded () trước một hình ảnh động. Việc thêm một trình bao bọc phụ vào UIWindow và gọi ba phương thức này trên đó đã khắc phục vấn đề của tôi.
masterwok

Tôi không nghĩ rằng việc gọi bố cục IfNeeded ngay sau khi setNeedLayout là chính xác. Bởi vì các phương thức làm tương tự mặc dù thực tế là một phương thức khiến bố cục được vẽ lại ngay lập tức và phương thức thứ hai trong chu kỳ cập nhật tiếp theo.
fillky
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.