Tạo các ràng buộc bố cục theo lập trình


84

Tôi biết rằng rất nhiều người đã đặt rất nhiều câu hỏi về vấn đề này, nhưng ngay cả với những câu trả lời tôi cũng không thể làm cho nó hoạt động.

Khi tôi xử lý các ràng buộc trên bảng phân cảnh, điều đó thật dễ dàng nhưng trong đoạn mã thì tôi gặp khó khăn. Ví dụ, tôi thử để có một chế độ xem nằm ở phía bên phải và có chiều cao của màn hình theo hướng màn hình. Đây là mã của tôi:

UIView *myView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 200, 748)];
myView.backgroundColor = [UIColor redColor];
[self.view addSubview:myView];
[self.view addConstraints:[NSLayoutConstraint
    constraintsWithVisualFormat:@"V:|-[myView(>=748)]-|"
    options:0 metrics:nil
    views:NSDictionaryOfVariableBindings(myView)]];

Nó không đáp ứng một số ràng buộc. Tôi không thấy có gì sai. Ngoài ra, tại sao tôi không thể sử dụng thuộc tính like self.myViewthay vì biến cục bộ như myView?


Vivi trong đoạn mã trên là gì?
iDev

14
Vui lòng đổi tiêu đề thành "iOS" thay vì "IOS" - Tiêu đề sau đề cập đến hệ điều hành bộ định tuyến / chuyển mạch của Cisco. Làm tôi bối rối. Cảm ơn.
armani

1
Một số câu hỏi về điều này: (1) Bạn đang gặp lỗi gì khi nó "không thỏa mãn một số ràng buộc"? (2) Bạn có đang dịch tự động lưu trữ thành các ràng buộc trên myViewkhông? (3) Cái gì vivi(như ACB đã hỏi)? (4) Bạn có tài sản được kê myViewkhai trên selfkhông?
Tim

1
ih Tim, xin lỗi vivi, tôi gõ quá nhanh ... đó là myView. Bạn dịch nghĩa là gì? Tất cả những gì tôi làm đều dựa trên đoạn mã trên. Về thuộc tính, tôi có một số chế độ xem khác được khai báo là thuộc tính nhưng khi tôi triển khai cùng một mã cho thuộc tính đó thì nó bị lỗi ... thông báo "error" thực sự rất lớn, tôi không thể dán nó vào đây
Pierre23

Để loại bỏ những hạn chế từ một cái nhìn đặc biệt tạm thời sau đó làm điều này stackoverflow.com/questions/13388104/...
Sam B

Câu trả lời:


106

Khi sử dụng Bố cục Tự động trong mã, việc thiết lập khung không làm gì cả. Vì vậy, việc bạn chỉ định chiều rộng 200 trên khung nhìn ở trên, không có ý nghĩa gì khi bạn đặt các ràng buộc trên đó. Để tập hợp ràng buộc của một chế độ xem là rõ ràng, nó cần bốn điều: vị trí x, vị trí y, chiều rộng và chiều cao cho bất kỳ trạng thái nhất định nào.

Hiện tại trong đoạn mã trên, bạn chỉ có hai (chiều cao, liên quan đến chế độ xem siêu cao và vị trí y, liên quan đến chế độ xem siêu cao). Ngoài ra, bạn có hai ràng buộc bắt buộc có thể xung đột tùy thuộc vào cách thiết lập các ràng buộc của chế độ xem siêu cấp của chế độ xem. Nếu superview có một ràng buộc bắt buộc chỉ định chiều cao của nó là một giá trị nào đó nhỏ hơn 748, bạn sẽ nhận được ngoại lệ "các ràng buộc không thỏa mãn".

Thực tế là bạn đã thiết lập chiều rộng của khung nhìn trước khi thiết lập các ràng buộc chẳng có nghĩa gì. Nó thậm chí sẽ không tính đến khung cũ và sẽ tính toán khung mới dựa trên tất cả các ràng buộc mà nó đã chỉ định cho các khung nhìn đó. Khi xử lý tự động thanh toán trong mã, tôi thường chỉ tạo một chế độ xem mới bằng cách sử dụng initWithFrame:CGRectZerohoặc đơn giản init.

Để tạo tập hợp ràng buộc cần thiết cho bố cục mà bạn đã mô tả bằng lời trong câu hỏi của mình, bạn sẽ cần thêm một số ràng buộc ngang để ràng buộc chiều rộng và vị trí x để cung cấp bố cục được chỉ định đầy đủ:

[self.view addConstraints:[NSLayoutConstraint
    constraintsWithVisualFormat:@"V:|-[myView(>=748)]-|"
    options:NSLayoutFormatDirectionLeadingToTrailing
    metrics:nil
    views:NSDictionaryOfVariableBindings(myView)]];

[self.view addConstraints:[NSLayoutConstraint
    constraintsWithVisualFormat:@"H:[myView(==200)]-|"
    options:NSLayoutFormatDirectionLeadingToTrailing
    metrics:nil
    views:NSDictionaryOfVariableBindings(myView)]];

Mô tả bằng lời bố cục này đọc như sau, bắt đầu với ràng buộc dọc:

myView sẽ lấp đầy chiều cao của superview bằng một phần đệm trên và dưới bằng không gian tiêu chuẩn. superview của myView có chiều cao tối thiểu là 748pts. Chiều rộng của myView là 200pts và có phần đệm bên phải bằng không gian tiêu chuẩn so với superview của nó.

Nếu bạn chỉ muốn chế độ xem lấp đầy toàn bộ chiều cao của chế độ xem siêu tốc mà không hạn chế chiều cao của chế độ xem siêu tốc, thì bạn chỉ cần bỏ qua (>=748)tham số trong văn bản định dạng trực quan. Nếu bạn nghĩ rằng (>=748)tham số là bắt buộc để cung cấp cho nó chiều cao - thì bạn không làm như vậy trong trường hợp này: ghim chế độ xem vào các cạnh của chế độ xem bằng cách sử dụng cú pháp bar ( |) hoặc thanh có dấu cách ( |-, -|), bạn đang cung cấp cho chế độ xem của mình -position (ghim chế độ xem trên một cạnh) và vị trí y với chiều cao (ghim chế độ xem trên cả hai cạnh), do đó thỏa mãn bộ ràng buộc của bạn cho chế độ xem.

Liên quan đến câu hỏi thứ hai của bạn:

Sử dụng NSDictionaryOfVariableBindings(self.myView)(nếu bạn đã thiết lập thuộc tính cho myView) và đưa nó vào VFL để sử dụng self.myViewtrong văn bản VFL của bạn, bạn có thể sẽ nhận được một ngoại lệ khi autolayout cố gắng phân tích cú pháp văn bản VFL của bạn. Nó liên quan đến ký hiệu dấu chấm trong các khóa từ điển và hệ thống đang cố gắng sử dụng valueForKeyPath:. Xem ở đây cho một câu hỏi và câu trả lời tương tự .


Đừng quên một số đối tượng như UILabel có kích thước nội tại và bạn không phải đặt chiều rộng hoặc chiều cao của đối tượng đó, chỉ có vị trí của nó. Tuy nhiên, nếu bạn đặt chiều cao hoặc chiều rộng, tôi tin rằng nó sẽ loại bỏ cả hai và sau đó bạn phải cung cấp cả hai.
Nick Turner

Mặc dù câu trả lời này không trả lời được câu hỏi của tôi, nhưng nó đã giúp tôi nhận ra điều gì đó. Cảm ơn vì đã tồn tại! :)
Matej

1
Giới thiệu bố cục tự động ngắn gọn và có thể đọc được duy nhất trên internet cho đến nay.
Joey Carson

plz giúp tôi câu hỏi của tôi là thế này ... Tôi không biết làm thế nào để sử dụng hạn chế lập trình stackoverflow.com/questions/36326288/...

61

Xin chào, tôi đã sử dụng trang này rất nhiều cho các ràng buộc và "cách thực hiện". Phải mất mãi mãi tôi mới nhận ra mình cần:

myView.translatesAutoresizingMaskIntoConstraints = NO;

để làm cho ví dụ này hoạt động. Cảm ơn Userxxx, Rob M. và đặc biệt là larsacus vì lời giải thích và mã ở đây, nó là vô giá.

Đây là mã đầy đủ để chạy các ví dụ ở trên:

UIView *myView = [[UIView alloc] init];
myView.backgroundColor = [UIColor redColor];
myView.translatesAutoresizingMaskIntoConstraints = NO;  //This part hung me up 
[self.view addSubview:myView];
//needed to make smaller for iPhone 4 dev here, so >=200 instead of 748
[self.view addConstraints:[NSLayoutConstraint
                           constraintsWithVisualFormat:@"V:|-[myView(>=200)]-|"
                           options:NSLayoutFormatDirectionLeadingToTrailing
                           metrics:nil
                           views:NSDictionaryOfVariableBindings(myView)]];

[self.view addConstraints:[NSLayoutConstraint
                           constraintsWithVisualFormat:@"H:[myView(==200)]-|"
                           options:NSLayoutFormatDirectionLeadingToTrailing
                           metrics:nil
                           views:NSDictionaryOfVariableBindings(myView)]];

11

Phiên bản Swift

Đã cập nhật cho Swift 3

Ví dụ này sẽ hiển thị hai phương pháp để thêm các ràng buộc sau theo chương trình giống như khi thực hiện trong Trình tạo giao diện:

Chiều rộng và chiều cao

nhập mô tả hình ảnh ở đây

Trung tâm trong vùng chứa

nhập mô tả hình ảnh ở đây

Mã Boilerplate

override func viewDidLoad() {
    super.viewDidLoad()

    // set up the view
    let myView = UIView()
    myView.backgroundColor = UIColor.blue
    myView.translatesAutoresizingMaskIntoConstraints = false
    view.addSubview(myView)

    // Add constraints code here (choose one of the methods below)
    // ...
}

Phương pháp 1: Kiểu neo

// width and height
myView.widthAnchor.constraint(equalToConstant: 200).isActive = true
myView.heightAnchor.constraint(equalToConstant: 100).isActive = true

// center in container
myView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
myView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true

Phương pháp 2: Kiểu NSLayoutConstraint

// width and height
NSLayoutConstraint(item: myView, attribute: NSLayoutAttribute.width, relatedBy: NSLayoutRelation.equal, toItem: nil, attribute: NSLayoutAttribute.notAnAttribute, multiplier: 1, constant: 200).isActive = true
NSLayoutConstraint(item: myView, attribute: NSLayoutAttribute.height, relatedBy: NSLayoutRelation.equal, toItem: nil, attribute: NSLayoutAttribute.notAnAttribute, multiplier: 1, constant: 100).isActive = true

// center in container
NSLayoutConstraint(item: myView, attribute: NSLayoutAttribute.centerX, relatedBy: NSLayoutRelation.equal, toItem: view, attribute: NSLayoutAttribute.centerX, multiplier: 1, constant: 0).isActive = true
NSLayoutConstraint(item: myView, attribute: NSLayoutAttribute.centerY, relatedBy: NSLayoutRelation.equal, toItem: view, attribute: NSLayoutAttribute.centerY, multiplier: 1, constant: 0).isActive = true

Ghi chú

  • Anchor style là phương pháp được ưa thích hơn NSLayoutConstraintStyle, tuy nhiên nó chỉ khả dụng từ iOS 9, vì vậy nếu bạn đang hỗ trợ iOS 8 thì bạn vẫn nên sử dụng NSLayoutConstraintStyle.
  • Xem thêm tài liệu Ràng buộc tạo theo chương trình .
  • Xem câu trả lời này để biết ví dụ tương tự về việc thêm ràng buộc ghim.

5

Về câu hỏi thứ hai của bạn về thuộc tính, bạn chỉ có thể sử dụng self.myViewnếu bạn đã khai báo nó là thuộc tính trong lớp. Vì myViewlà một biến cục bộ, bạn không thể sử dụng nó theo cách đó. Để biết thêm chi tiết về điều này, tôi khuyên bạn nên xem qua tài liệu của apple về Thuộc tính được khai báo ,


5

tại sao tôi không thể sử dụng một thuộc tính như self.myViewthay vì một biến cục bộ như myView?

thử sử dụng:

NSDictionaryOfVariableBindings(_view)

thay vì self.view


Điều đó khiến ứng dụng của tôi gặp sự cố, nó đưa ra thông báo: 'Không thể phân tích cú pháp định dạng ràng buộc: polylineImageView không phải là một khóa trong từ điển chế độ xem.'
Tai Le

4

Cũng xin lưu ý rằng từ iOS9, chúng tôi có thể xác định các ràng buộc theo chương trình "ngắn gọn hơn và dễ đọc hơn" bằng cách sử dụng các lớp con của lớp trợ giúp mới NSLayoutAnchor .

Một ví dụ từ tài liệu:

[self.cancelButton.leadingAnchor constraintEqualToAnchor:self.saveButton.trailingAnchor constant: 8.0].active = true;
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.