Tôi nên đặt các ràng buộc tự động thanh toán ở đâu khi tạo chế độ xem theo chương trình


79

Tôi thấy các ví dụ khác nhau trong đó các ràng buộc được thiết lập. Một số đặt chúng trong viewDidLoad/ loadView(sau khi lượt xem phụ được thêm vào). Những người khác đặt chúng trong phương thức updateViewConstraints, được gọi bởi viewDidAppear.

Khi tôi thử đặt các ràng buộc trong updateViewContraintsđó, bố cục có thể bị giật, ví dụ như độ trễ nhỏ trước khi chế độ xem xuất hiện. Ngoài ra, nếu tôi sử dụng phương pháp này, tôi có nên xóa các ràng buộc hiện có trước [self.view [removeConstraints:self.view.constraints]không?


1
Tôi đã có trải nghiệm tương tự với updateViewConstraints, vì vậy tôi đã ngừng cố gắng sử dụng nó. Tôi định cấu hình các ràng buộc trong viewDidLoad hoặc trong phương thức updateConstraints của một chế độ xem tùy chỉnh. Mong rằng sẽ có ai đó trả lời dứt khoát cho bạn.
bilobatum

1
updateViewConstraints: Bạn có thể ghi đè phương thức này trong một lớp con để thêm các ràng buộc vào dạng xem hoặc các lần xem con của nó. (từ Apple docs )
thử nghiệm

Câu trả lời:


108

Tôi đã thiết lập các ràng buộc của mình trong viewDidLoad/ loadView(Tôi đang nhắm mục tiêu iOS> = 6). updateViewConstraintsrất hữu ích cho việc thay đổi giá trị của các ràng buộc, ví dụ: nếu một số ràng buộc phụ thuộc vào hướng của màn hình (tôi biết, đó là một cách làm không tốt), bạn có thể thay đổi nó constanttrong phương pháp này.

Việc thêm các ràng buộc vào viewDidLoadđược hiển thị trong phiên "Giới thiệu về Bố cục Tự động cho iOS và OS X" (WWDC 2012), bắt đầu từ 39:22. Tôi nghĩ đó là một trong những điều được nói trong các bài giảng nhưng không có trong tài liệu.

CẬP NHẬT: Tôi đã nhận thấy đề cập đến việc thiết lập các ràng buộc trong Quản lý tài nguyên trong Bộ điều khiển chế độ xem :

Nếu bạn muốn tạo chế độ xem theo lập trình, thay vì sử dụng bảng phân cảnh, bạn làm như vậy bằng cách ghi đè loadView phương thức của bộ điều khiển chế độ xem của bạn . Việc triển khai phương pháp này của bạn phải thực hiện như sau:

(...)

3.Nếu bạn đang sử dụng bố cục tự động, hãy gán đủ các ràng buộc cho mỗi dạng xem bạn vừa tạo để kiểm soát vị trí và kích thước của dạng xem của bạn . Nếu không, hãy triển khai viewWillLayoutSubviewsviewDidLayoutSubviewscác phương pháp để điều chỉnh khung của các chế độ xem phụ trong phân cấp chế độ xem. Xem “Thay đổi kích thước các chế độ xem của View Controller.”

CẬP NHẬT 2 : Trong WWDC 2015, Apple đã đưa ra giải thích mới về cách sử dụng updateConstraintsupdateViewConstraintskhuyến nghị:

Thực sự, tất cả những điều này là một cách để các chế độ xem có cơ hội thực hiện các thay đổi đối với các ràng buộc đúng lúc cho lần chuyển bố cục tiếp theo, nhưng nó thường không thực sự cần thiết.

Tất cả các thiết lập ràng buộc ban đầu của bạn lý tưởng nên diễn ra bên trong Trình tạo giao diện.

Hoặc nếu bạn thực sự thấy rằng bạn cần phân bổ các ràng buộc của mình theo lập trình, một số nơi như viewDidLoad sẽ tốt hơn nhiều.

Các ràng buộc cập nhật thực sự chỉ dành cho công việc cần được lặp lại định kỳ.

Ngoài ra, khá đơn giản là chỉ thay đổi các ràng buộc khi bạn thấy cần phải làm điều đó; ngược lại, nếu bạn tách logic đó ra khỏi mã khác có liên quan đến nó và bạn chuyển nó sang một phương thức riêng biệt sẽ được thực thi sau đó, thì mã của bạn sẽ trở nên khó theo dõi hơn rất nhiều, vì vậy bạn sẽ khó bảo trì hơn , người khác sẽ khó hiểu hơn rất nhiều.

Vậy khi nào bạn cần sử dụng các ràng buộc cập nhật?

Chà, nó tổng hợp thành hiệu suất.

Nếu bạn thấy rằng việc chỉ thay đổi các ràng buộc tại chỗ là quá chậm, thì các ràng buộc cập nhật có thể giúp bạn.

Nó chỉ ra rằng việc thay đổi một ràng buộc bên trong các ràng buộc cập nhật thực sự nhanh hơn việc thay đổi một ràng buộc vào những thời điểm khác.

Lý do cho điều đó là bởi vì động cơ có thể coi tất cả các thay đổi ràng buộc xảy ra trong lần vượt qua này là một đợt.


2
+1 cho đây là câu trả lời tốt nhất. Apple quy định loadView là nơi chính xác để đặt các ràng buộc ban đầu và điều này không yêu cầu thêm cờ BOOL trong phương thức updateConstraints (có vẻ như hacky).
awolf

4
Tôi quan điểm của tôi, chế độ xem nên chịu trách nhiệm cho các ràng buộc, không phải bộ điều khiển chế độ xem. Trong nhiều trường hợp, bộ điều khiển chế độ xem thậm chí không biết tất cả các phần tử trong chế độ xem là gì (ví dụ: nhãn tĩnh trong ô chế độ xem bảng).
dasdom

1
@dasdom Chế độ xem có thể kiểm soát mối quan hệ của nó với các chế độ xem khác như thế nào? Bạn phải thiết lập các ràng buộc như @"|-[button1]-[button2]-|"trong ViewController, phải không? Hay là có một cách khác?
Joseph

@Casper Hầu hết các lần tôi có một lớp con UIView, đó là chế độ xem của bộ điều khiển chế độ xem. Trong chế độ xem đó có các lượt xem phụ và các ràng buộc đối với các lượt xem phụ.
dasdom

3
Tại sao việc thay đổi các ràng buộc trong là một cách làm không tốt updateViewConstraints?
kiểm tra

33

Tôi khuyên bạn nên tạo BOOL và đặt chúng trong -updateConstraintsUIView (hoặc -updateViewConstraints, cho UIViewController).

-[UIView updateConstraints]: (apple docs)

Các dạng xem tùy chỉnh tự thiết lập các ràng buộc nên làm như vậy bằng cách ghi đè phương thức này.

Cả hai -updateConstraints-updateViewConstraintscó thể được gọi nhiều lần trong thời gian tồn tại của một chế độ xem. ( setNeedsUpdateConstraintsVí dụ: gọi trên một dạng xem sẽ kích hoạt điều này xảy ra.) Do đó, bạn cần đảm bảo ngăn việc tạo và kích hoạt các ràng buộc trùng lặp - hoặc sử dụng BOOL để chỉ thực hiện thiết lập ràng buộc nhất định chỉ một lần hoặc bằng cách đảm bảo để hủy kích hoạt / loại bỏ các ràng buộc hiện có trước khi tạo và kích hoạt các ràng buộc mới.

Ví dụ:

  - (void)updateConstraints {  // for view controllers, use -updateViewConstraints

         if (!_hasLoadedConstraints) {
              _hasLoadedConstraints = YES;
             // create your constraints
         }
         [super updateConstraints];
    }

Chúc mừng @fresidue trong các nhận xét vì đã chỉ ra rằng tài liệu của Apple khuyên bạn nên gọi superlà bước cuối cùng. Nếu bạn gọi supertrước khi thực hiện thay đổi đối với một số ràng buộc, bạn có thể gặp phải ngoại lệ thời gian chạy (sự cố).


7
Tôi không chắc liệu nó có tạo ra sự khác biệt nào về mặt thực tế hay không, nhưng tài liệu cho biết 'Quan trọng: Gọi [super updateConstraints] là bước cuối cùng trong quá trình triển khai của bạn.'
freshidue

Theo tài liệu, nếu bạn thay đổi chế độ xem trong thời gian chạy và làm mất hiệu lực các ràng buộc của mình thì hệ thống sẽ ngay lập tức loại bỏ ràng buộc đó và gọi setNeedsUpdateConstraints. Trước khi một bố cục mới được thực hiện, hệ thống sẽ gọi updateConstraints nơi bạn có thể tùy chỉnh bố cục đã bị vô hiệu hóa. Như vậy, có lẽ tôi sẽ không đặt cờ trên phương thức này có thể ngăn hệ thống gọi nó.
smileBot

1
@cocoanutmobile Nếu bạn sử dụng cờ BOOL như được đề xuất trong câu trả lời này, nó chỉ để ngăn một số ràng buộc của bạn bị thêm nhiều lần. Đó là một điều hoàn toàn tốt để làm. Một cách thay thế khác là chỉ cần lưu trữ một tham chiếu đến bất kỳ ràng buộc nào bạn tạo và sau đó đảm bảo xóa (hủy kích hoạt) tất cả những ràng buộc đó trước khi bạn tạo và kích hoạt bất kỳ ràng buộc mới nào. Tuy nhiên, cách tiếp cận này sẽ mang lại hiệu suất kém hơn, đặc biệt nếu các ràng buộc cũ và mới của bạn giống hệt nhau.
smileyborg

2
@cocoanutmobile Ngoài ra, hãy lưu ý rằng cờ BOOL ở đây không ngăn hệ thống gọi -updateConstraintshoặc bất cứ điều gì - nó sẽ vẫn được gọi và bạn vẫn gọi [super updateConstraints].
smileyborg

1
Như @fresidue đã đề cập, hãy nhớ gọi supervào cuối quá trình triển khai -updateConstraintshoặc -updateViewConstraints. Xem bình luận này để biết thêm thông tin.
smileyborg

4

Điều này sẽ được thực hiện trong ViewDidLoad, theo video WWDC từ Apple và tài liệu.

Không biết tại sao mọi người lại đề xuất updateConstraints. Nếu bạn thực hiện trong updateConstraints, bạn sẽ gặp sự cố với NSAutoresizingMaskLayoutConstraint với tính năng tự động thay đổi kích thước vì chế độ xem của bạn đã tính đến mặt nạ tự động. Bạn sẽ cần xóa chúng trong updateConstraints để hoạt động.

UpdateConstraints chỉ dành cho điều đó, khi bạn cần 'cập nhật' chúng, hãy thực hiện các thay đổi, v.v. từ thiết lập ban đầu của bạn.


1
Bạn có thể thêm các liên kết cho video và tài liệu bạn đang tham khảo tại đây.
Shivam Pokhriyal

2

Do it in view did layouts subviews method

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
}

1

Tôi có giải pháp này để thay đổi các ràng buộc trước khi những người trong bảng phân cảnh được tải. Giải pháp này loại bỏ bất kỳ độ trễ nào sau khi chế độ xem được tải.

-(void)updateViewConstraints{

    dispatch_async(dispatch_get_main_queue(), ^{

            //Modify here your Constraint -> Activate the new constraint and deactivate the old one

            self.yourContraintA.active = true;
            self.yourContraintB.active= false;
            //ecc..
           });

    [super updateViewConstraints]; // This must be the last thing that you do here -> if! ->Crash!
}

1

Bạn cũng có thể đặt chúng trong viewWillLayoutSubviews :

 override func viewWillLayoutSubviews() {

    if(!wasViewLoaded){
        wasViewLoaded = true

        //update constraint

        //also maybe add a subview            
    }
}

0

Điều này đã làm việc cho tôi:

Swift 4.2

override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)

// Modify your constraints in here

  ...

}

Mặc dù thành thật mà nói tôi không chắc liệu nó có xứng đáng hay không. Có vẻ như tải chậm hơn một chút so với trong viewDidLoad (). Tôi chỉ muốn chuyển chúng ra khỏi cái sau, vì nó ngày càng lớn.


0

Ví dụ sau là chuyển bất kỳ chế độ xem nào cho một lớp khác. tạo chế độ xem của tôi từ bảng phân cảnh

Swift 5.0

    override func viewWillAppear(_ animated: Bool) {
        
      super.viewWillAppear(animated) 
        DispatchQueue.main.async {
            self.abcInstance = ABC(frame: self.myView.frame)
          } 
      }

 

Nếu bạn bỏ lỡ DispatchQueue.main.async, sẽ mất thời gian để cập nhật các ràng buộc trong viewWillAppear. Tạo myView trong bảng phân cảnh và đưa ra các ràng buộc giống như chiều rộng và chiều cao của màn hình, sau đó thử in khung của myView. nó sẽ cung cấp giá trị chính xác trong DispatchQueue.main.async hoặc trong viewDidAppear nhưng không cung cấp giá trị chính xác trong viewWillAppear mà không có DispatchQueue.main.async.

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.