Làm cách nào để thay đổi mức độ ưu tiên của các ràng buộc trong thời gian chạy


86

Tôi có một chế độ xem có chiều cao động và tôi đang cố gắng thay đổi mức độ ưu tiên của chế độ xem này trong thời gian chạy.

Đây là phần mã của tôi;

if (index == 0) {

    surveyViewHeightConstraint.constant = 0;
    surveyViewHeightConstraint.priority = 1000;

} else if (index == 1) {

    surveyViewHeightConstraint.constant = 163;
    surveyViewHeightConstraint.priority = 500;

}

Tôi đang thay đổi chỉ mục bằng một hành động nút. Khi tôi chạy mã này, tôi gặp lỗi sau:

*** Assertion failure in -[NSLayoutConstraint setPriority:], /SourceCache/Foundation/Foundation-1141.1/Layout.subproj/NSLayoutConstraint.m:174

Sai lầm của tôi ở đây là gì?

Câu trả lời:


173

Như đã nêu trong NSLayoutConstrainttài liệu tham khảo của lớp :

Các ưu tiên có thể không thay đổi từ không bắt buộc thành bắt buộc, hoặc từ bắt buộc sang không bắt buộc. Một ngoại lệ sẽ được đưa ra nếu mức độ ưu tiên NSLayoutPriorityRequiredtrong OS X hoặc UILayoutPriorityRequiredtrong iOS được thay đổi thành mức độ ưu tiên thấp hơn hoặc nếu mức độ ưu tiên thấp hơn được thay đổi thành mức độ ưu tiên bắt buộc sau khi các ràng buộc được thêm vào chế độ xem. Cho phép thay đổi từ mức ưu tiên tùy chọn này sang mức ưu tiên tùy chọn khác ngay cả sau khi đã cài đặt ràng buộc trên một dạng xem.

Sử dụng mức độ ưu tiên 999 thay vì 1000. Về mặt kỹ thuật, nó sẽ không hoàn toàn bắt buộc, nhưng nó sẽ là mức độ ưu tiên cao hơn bất kỳ thứ gì khác.


4
Cảm ơn câu trả lời hay của bạn, nhưng nếu tôi sử dụng 999 thay vì 1000, tôi không thể ẩn chế độ xem của mình bằng cách gán 0 cho giới hạn chiều cao.
Le'Kirdok

Đó là một vấn đề khác. Bạn có thể có những ràng buộc mâu thuẫn khác. Nếu mã của bạn không bị lỗi nữa nhưng hoạt ảnh xem của bạn vẫn không chính xác, vui lòng đánh dấu câu hỏi này là đã được giải quyết; sau đó mở một cái khác.
Cyrille

đẹp nhất, nó không phải là một vấn đề để sử dụng giá trị khác nhau (999, 900, 500, vv) :)
user924

7
Nghiêm túc? Có điều gì hoạt động bình thường trong quá trình phát triển iOS không? Vì vậy, nhiều thứ phải được thực hiện theo cách thủ công và / hoặc phải triển khai hàng tấn mã giải pháp, nguyên nhân gây ra lỗi hệ thống hoặc không được hỗ trợ. Xin lỗi vì nhận xét không mang tính xây dựng.
Luten

6
Có vẻ như giới hạn này đã bị loại bỏ trong iOS 13. Tôi đã thử thay đổi một hạn chế từ requiredthành defaultLowvà nó đã hoạt động. Mã tương tự được sử dụng để gặp sự cố trên iOS 12.
Steven Vandeweghe

52

Tôi muốn bổ sung một chút cho câu trả lời của Cyrille.

Nếu bạn đang tạo ràng buộc trong mã, hãy đảm bảo đặt mức độ ưu tiên của nó trước khi kích hoạt nó. Ví dụ:

surveyViewHeightConstraint = [NSLayoutConstraint constraintWithItem:self
                                               attribute:NSLayoutAttributeHeight
                                               relatedBy:NSLayoutRelationEqual
                                                  toItem:self.superview
                                               attribute:NSLayoutAttributeHeight
                                              multiplier:1
                                                constant:0];
surveyViewHeightConstraint.active = YES;
surveyViewHeightConstraint.priority = 999;

Điều này sẽ dẫn đến ngoại lệ thời gian chạy.

Không hỗ trợ thay đổi mức độ ưu tiên từ bắt buộc thành không trên một ràng buộc đã cài đặt (hoặc ngược lại).

Thứ tự đúng là:

surveyViewHeightConstraint.priority = 999;
surveyViewHeightConstraint.active = YES;

Đối với Swift phiên bản 4+

constraintName.priority = UILayoutPriority(rawValue: 999)

13

Phiên bản Swift:

myContraint.priority = UILayoutPriority(999.0)

8

Cách chúng tôi luôn xử lý điều này là không thay đổi hằng số ràng buộc, chỉ ưu tiên. Ví dụ, trong tình huống của bạn có hai hạn chế về chiều cao.

 heightConstraintOne (who's height is set in the storyboard at 150, and priority set at 750)
 heightConstraintTwo (who's height is set in the storyboard at 0, and priority set at 250)

nếu bạn muốn ẩn chế độ xem, bạn:

heightConstraintTwo.priority = 999;

tương tự như vậy, nếu bạn muốn hiển thị chế độ xem:

heightConstraintTwo.priority = 250;

5
Tuyệt vời, hiểu rồi: sử dụng 999 thay vì 1000. Cảm ơn
brainray

3

Tôi hy vọng kịch bản này sẽ giúp được ai đó: Tôi có hai hạn chế, đó là chúng đối lập nhau, ý tôi là chúng không thể hài lòng cùng một lúc, trong trình tạo giao diện, một trong số chúng có mức ưu tiên 1000 và người khác có mức ưu tiên ít hơn 1000. khi tôi thay đổi chúng trong mã, tôi gặp lỗi này:

Việc chấm dứt ứng dụng do không có ngoại lệ 'NSInternalInconsistencyException', lý do: 'Thay đổi mức độ ưu tiên từ bắt buộc thành không trên một ràng buộc đã cài đặt (hoặc ngược lại) không được hỗ trợ. Bạn đã vượt qua mức độ ưu tiên 250 và mức độ ưu tiên hiện tại là 1000. '

thì tôi vừa khởi tạo chúng trong hàm viewDidLoad với các giá trị .defaultHigh và .defaultLow và điều này đã khắc phục sự cố của tôi.


1

Theo NSLayoutConstraints classtrong UIKit Module

Nếu mức độ ưu tiên của ràng buộc nhỏ hơn UILayoutPosystemRequired thì nó là tùy chọn. Các ràng buộc ưu tiên cao hơn được đáp ứng trước các ràng buộc ưu tiên thấp hơn. Sự thỏa mãn ràng buộc không phải là tất cả hoặc không có gì. Nếu một ràng buộc 'a == b' là tùy chọn, điều đó có nghĩa là chúng tôi sẽ cố gắng giảm thiểu 'abs (ab)'. Thuộc tính này chỉ có thể được sửa đổi như một phần của thiết lập ban đầu hoặc khi tùy chọn. Sau khi một ràng buộc đã được thêm vào một dạng xem, một ngoại lệ sẽ được ném ra nếu mức độ ưu tiên được thay đổi từ / thành NSLayoutPosystemRequired.

Ví dụ: - các UIButtonràng buộc với các ưu tiên khác nhau -

 func setConstraints() {
        buttonMessage.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint(item: buttonMessage, attribute: .bottom, relatedBy: .equal, toItem: view, attribute: .bottom, multiplier: 1.0, constant: -10).isActive = true

        let leading = NSLayoutConstraint(item: buttonMessage, attribute: .leading, relatedBy: .equal, toItem: view, attribute: .leading, multiplier: 1.0, constant: 10)

        leading.isActive = true


        let widthConstraint = NSLayoutConstraint(item: buttonMessage, attribute: NSLayoutAttribute.width, relatedBy: NSLayoutRelation.equal, toItem: nil, attribute: NSLayoutAttribute.notAnAttribute, multiplier: 1, constant: 100)

        let heightConstraint = NSLayoutConstraint(item: buttonMessage, attribute: NSLayoutAttribute.height, relatedBy: NSLayoutRelation.equal, toItem: nil, attribute: NSLayoutAttribute.notAnAttribute, multiplier: 1, constant: 50)


        let trailingToSuperView = NSLayoutConstraint(item: buttonMessage, attribute: .trailing, relatedBy: .equal, toItem: view, attribute: .trailing, multiplier: 1, constant: 0)

        trailingToSuperView.priority = 999
        trailingToSuperView.isActive = true

        //leading.isActive = false//uncomment & check it will align to right of the View

        buttonMessage.addConstraints([widthConstraint,heightConstraint])

         }  

1
Không, bạn không bao giờ nên tạo ra các ràng buộc trong viewDidLayoutSubviews. Phương thức đó có thể được gọi nhiều lần, có thể làm hỏng ứng dụng của bạn nếu bạn tạo các ràng buộc trùng lặp ở đó.
mph

0

Tôi đã phải đối mặt với cùng một vấn đề. Như một số câu trả lời đã đề cập ở trên trong iOS 13, việc thay đổi mức độ ưu tiên sẽ hoạt động tốt, Tuy nhiên, trong iOS 12, nó sẽ dẫn đến sự cố.

Tôi đã có thể khắc phục sự cố này bằng cách tạo IBOutlet NSLayoutConstraint đến ràng buộc cụ thể đó trong Storyboard giữ mức ưu tiên của nó thành 1000, dưới đây là mã để khắc phục.

if (Condition) {
    surveyViewHeightConstraint.constant = 0;
    surveyViewHeightConstraint.isActive = false;
} else if (index == 1) {
    surveyViewHeightConstraint.constant = 163;
    surveyViewHeightConstraint.isActive = True;
}

Hi vọng điêu nay co ich!!! Chúc mừng


-3

Bây giờ bạn có thể, chỉ cần lấy kết nối IBOutlet cho các ràng buộc của bạn, sau đó sử dụng mã này.

self.webviewHeight.priority = UILayoutPriority(rawValue: 1000)
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.