Chiều rộng và Chiều cao Bằng với superView của nó bằng cách sử dụng tự động thanh toán theo chương trình?


83

Tôi đã tìm kiếm rất nhiều đoạn mã trên mạng và tôi vẫn không thể tìm thấy câu trả lời cho vấn đề của mình. Câu hỏi của tôi là tôi có một scrollView (SV) và tôi muốn thêm một nút vào bên trong scrollView (SV) theo chương trình với cùng chiều rộng và chiều cao của superview của nó là scrollView (SV) để khi người dùng xoay nút thiết bị sẽ có cùng một khung của scrollView (SV). làm thế nào để thực hiện NSLayout / NSLayoutConstraint? cảm ơn

Câu trả lời:


125

Nếu ai đó đang tìm kiếm một giải pháp Swift - tôi sẽ tạo một tiện ích mở rộngUIView Swift để giúp bạn mỗi khi bạn muốn liên kết khung chế độ xem phụ với giới hạn chế độ xem siêu cao của nó:

Swift 2:

extension UIView {

    /// Adds constraints to this `UIView` instances `superview` object to make sure this always has the same size as the superview.
    /// Please note that this has no effect if its `superview` is `nil` – add this `UIView` instance as a subview before calling this.
    func bindFrameToSuperviewBounds() {
        guard let superview = self.superview else {
            print("Error! `superview` was nil – call `addSubview(view: UIView)` before calling `bindFrameToSuperviewBounds()` to fix this.")
            return
        }

        self.translatesAutoresizingMaskIntoConstraints = false
        superview.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-0-[subview]-0-|", options: .DirectionLeadingToTrailing, metrics: nil, views: ["subview": self]))
        superview.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-0-[subview]-0-|", options: .DirectionLeadingToTrailing, metrics: nil, views: ["subview": self]))
    }

}

Swift 3:

extension UIView {

    /// Adds constraints to this `UIView` instances `superview` object to make sure this always has the same size as the superview.
    /// Please note that this has no effect if its `superview` is `nil` – add this `UIView` instance as a subview before calling this.
    func bindFrameToSuperviewBounds() {
        guard let superview = self.superview else {
            print("Error! `superview` was nil – call `addSubview(view: UIView)` before calling `bindFrameToSuperviewBounds()` to fix this.")
            return
        }

        self.translatesAutoresizingMaskIntoConstraints = false
        superview.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|-0-[subview]-0-|", options: .directionLeadingToTrailing, metrics: nil, views: ["subview": self]))
        superview.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|-0-[subview]-0-|", options: .directionLeadingToTrailing, metrics: nil, views: ["subview": self]))
    }
}

Swift 4.2:

extension UIView {

    /// Adds constraints to this `UIView` instances `superview` object to make sure this always has the same size as the superview.
    /// Please note that this has no effect if its `superview` is `nil` – add this `UIView` instance as a subview before calling this.
    func bindFrameToSuperviewBounds() {
        guard let superview = self.superview else {
            print("Error! `superview` was nil – call `addSubview(view: UIView)` before calling `bindFrameToSuperviewBounds()` to fix this.")
            return
        }

        self.translatesAutoresizingMaskIntoConstraints = false
        self.topAnchor.constraint(equalTo: superview.topAnchor, constant: 0).isActive = true
        self.bottomAnchor.constraint(equalTo: superview.bottomAnchor, constant: 0).isActive = true
        self.leadingAnchor.constraint(equalTo: superview.leadingAnchor, constant: 0).isActive = true
        self.trailingAnchor.constraint(equalTo: superview.trailingAnchor, constant: 0).isActive = true

    }
}

Sau đó, chỉ cần gọi nó như thế này :

// after adding as a subview, e.g. `view.addSubview(subview)`
subview.bindFrameToSuperviewBounds()

khi tạo một UIView tùy chỉnh sử dụng một .xib, các bindFrameToSuperviewBounds nên được gọi là chỉ trong vòng sau self.addSubview (self.view) 'init yêu cầu (coder aDecoder)?'
user1603721

Cần lưu ý rằng các giải pháp sử dụng định dạng trực quan không thân thiện với khu vực an toàn. Ví dụ: nếu bạn đang gọi điều này trong một chế độ xem bên trong bộ điều khiển điều hướng hiển thị các thanh điều hướng và thanh công cụ, thì chế độ xem của bạn sẽ nằm dưới thanh điều hướng và dưới thanh công cụ nếu nó đi xuống quá xa.
Andy Ibanez

Điều này cũng hoạt động như một giải pháp cho Swift 5. Tôi không thể làm cho Chế độ xem phụ tùy chỉnh của mình thích ứng với kích thước Chế độ xem phụ huynh bằng Tự động thanh toán. Sử dụng điều này sau khi lượt xem phụ được thêm vào hoạt động như một sự quyến rũ.
toni_piu

Giải pháp Swift 4.2 hoạt động tốt. Bạn thậm chí có thể làm cho nó ngắn hơn một chút bằng cách loại bỏ constant: 0một phần.
Zyphrax

69

Tôi không chắc liệu đây có phải là cách hiệu quả nhất để làm điều đó hay không, nhưng nó hoạt động ..

UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
button.translatesAutoresizingMaskIntoConstraints = NO;
// initialize


[coverForScrolView addSubview:button];

NSLayoutConstraint *width =[NSLayoutConstraint
                                    constraintWithItem:button
                                    attribute:NSLayoutAttributeWidth
                                    relatedBy:0
                                    toItem:coverForScrolView
                                    attribute:NSLayoutAttributeWidth
                                    multiplier:1.0
                                    constant:0];
NSLayoutConstraint *height =[NSLayoutConstraint
                                     constraintWithItem:button
                                     attribute:NSLayoutAttributeHeight
                                     relatedBy:0
                                     toItem:coverForScrolView
                                     attribute:NSLayoutAttributeHeight
                                     multiplier:1.0
                                     constant:0];
NSLayoutConstraint *top = [NSLayoutConstraint
                                   constraintWithItem:button
                                   attribute:NSLayoutAttributeTop
                                   relatedBy:NSLayoutRelationEqual
                                   toItem:coverForScrolView
                                   attribute:NSLayoutAttributeTop
                                   multiplier:1.0f
                                   constant:0.f];
NSLayoutConstraint *leading = [NSLayoutConstraint
                                       constraintWithItem:button
                                       attribute:NSLayoutAttributeLeading
                                       relatedBy:NSLayoutRelationEqual
                                       toItem:coverForScrolView
                                       attribute:NSLayoutAttributeLeading
                                       multiplier:1.0f
                                       constant:0.f];
[coverForScrolView addConstraint:width];
[coverForScrolView addConstraint:height];
[coverForScrolView addConstraint:top];
[coverForScrolView addConstraint:leading];

4
Nó sẽ được nhiều hiệu quả hơn để sử dụngNSLayoutConstraint.activateConstraints([width, height, top, leading])
Berik

Bạn có thể sử dụng[coverForScrolView addConstraints:@[width, height, top, leading]];
Islam Q.

1
điều đáng chú ý là (nhiều năm sau) mã này rất lỗi thời . nó bây giờ dễ dàng hơn để thêm khó khăn - xem bất kỳ câu trả lời dưới đây 2017
Fattie

49

Liên kết này có thể giúp bạn, hãy làm theo hướng dẫn: http://www.raywenderlich.com/20881/beginning-auto-layout-part-1-of-2

BIÊN TẬP :

sử dụng đoạn mã sau, trong đó subview là subivew của bạn.

[subview setTranslatesAutoresizingMaskIntoConstraints:NO];
[self.view addConstraints:[NSLayoutConstraint
                           constraintsWithVisualFormat:@"H:|-0-[subview]-0-|"
                           options:NSLayoutFormatDirectionLeadingToTrailing
                           metrics:nil
                           views:NSDictionaryOfVariableBindings(subview)]];
[self.view addConstraints:[NSLayoutConstraint
                           constraintsWithVisualFormat:@"V:|-0-[subview]-0-|"
                           options:NSLayoutFormatDirectionLeadingToTrailing
                           metrics:nil
                           views:NSDictionaryOfVariableBindings(subview)]];

4
Trong trường hợp này, các định dạng hình ảnh có thể là cũng V:|[subview]|H:|[subview]|
Gustavo Barbosa

4
điều đáng chú ý là (nhiều năm sau) mã này rất lỗi thời . nó bây giờ dễ dàng hơn để thêm khó khăn - xem bất kỳ câu trả lời dưới đây 2017
Fattie

19

addConstraintremoveConstraintcác phương thức cho UIView sẽ không còn được dùng nữa, vì vậy bạn nên sử dụng 'các tiện ích tạo ràng buộc':

view.topAnchor.constraint(equalTo: superView.topAnchor, constant: 0).isActive = true
view.bottomAnchor.constraint(equalTo: superView.bottomAnchor, constant: 0).isActive = true
view.leadingAnchor.constraint(equalTo: superView.leadingAnchor, constant: 0).isActive = true
view.trailingAnchor.constraint(equalTo: superView.trailingAnchor, constant: 0).isActive = true

Điều này hoạt động tốt. Bạn thậm chí có thể làm cho nó ngắn hơn một chút bằng cách loại bỏ constant: 0một phần.
Zyphrax

8

Cách tiếp cận # 1: Thông qua Tiện ích mở rộng UIView

Đây là một cách tiếp cận chức năng hơn trong Swift 3+ với điều kiện tiên quyết thay vì điều kiện tiên quyếtprint (có thể dễ dàng bị hỏng trong bảng điều khiển). Cái này sẽ báo lỗi lập trình viên là bản dựng không thành công.

Thêm tiện ích mở rộng này vào dự án của bạn:

extension UIView {
    /// Adds constraints to the superview so that this view has same size and position.
    /// Note: This fails the build if the `superview` is `nil` – add it as a subview before calling this.
    func bindEdgesToSuperview() {
        guard let superview = superview else {
            preconditionFailure("`superview` was nil – call `addSubview(view: UIView)` before calling `bindEdgesToSuperview()` to fix this.")
        }
        translatesAutoresizingMaskIntoConstraints = false
        ["H:|-0-[subview]-0-|", "V:|-0-[subview]-0-|"].forEach { visualFormat in
            superview.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: visualFormat, options: .directionLeadingToTrailing, metrics: nil, views: ["subview": self]))
        }
    }
}

Bây giờ chỉ cần gọi nó như thế này:

// after adding as a subview, e.g. `view.addSubview(subview)`
subview.bindEdgesToSuperview()

Lưu ý rằng phương pháp trên đã được tích hợp vào khuôn khổ HandyUIKit của tôi , nó cũng thêm một số trình trợ giúp UI tiện dụng hơn vào dự án của bạn.


Phương pháp tiếp cận # 2: Sử dụng Khung

Nếu bạn làm việc nhiều với các ràng buộc lập trình trong dự án của mình thì tôi khuyên bạn nên kiểm tra SnapKit . Nó giúp làm việc với các ràng buộc dễ dàng hơn rất nhiều và ít bị lỗi hơn .

Làm theo hướng dẫn cài đặt trong tài liệu để đưa SnapKit vào dự án của bạn. Sau đó, nhập nó ở đầu tệp Swift của bạn:

import SnapKit

Bây giờ bạn có thể đạt được điều tương tự chỉ với điều này:

subview.snp.makeConstraints { make in
    make.edges.equalToSuperview()
}

6

Swift 3:

import UIKit

extension UIView {

    func bindFrameToSuperviewBounds() {
        guard let superview = self.superview else {
            print("Error! `superview` was nil – call `addSubview(view: UIView)` before calling `bindFrameToSuperviewBounds()` to fix this.")
            return
        }

        self.translatesAutoresizingMaskIntoConstraints = false
        superview.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|-0-[subview]-0-|", options: .directionLeadingToTrailing, metrics: nil, views: ["subview": self]))
        superview.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|-0-[subview]-0-|", options: .directionLeadingToTrailing, metrics: nil, views: ["subview": self]))
    }

}

Nếu bạn chỉ thay đổi mã để làm cho nó tuân thủ Swift 3, bạn nên chỉnh sửa câu trả lời áp phích ban đầu thay vì đăng câu trả lời mới (vì đây không phải là thay đổi ý định của áp phích ban đầu). Nếu bạn không đủ điểm để chỉnh sửa, hãy bình luận về bài đăng gốc kèm theo gợi ý về những thay đổi cần thiết để tuân thủ Swift 3. Người đăng ban đầu (hoặc người khác nhìn thấy nhận xét của bạn) sau đó có thể sẽ cập nhật câu trả lời. Bằng cách này, chúng tôi giữ cho chủ đề sạch sẽ khỏi các câu trả lời trùng lặp và mã không dùng nữa.
Jeehut

Này @Dschee - Tôi hoàn toàn đồng ý với bạn, nhưng chúng tôi đã "sai". quan điểm "đồng thuận" trên trang web tốt hơn hay tệ hơn, ngược lại với những gì bạn thể hiện ở đây. meta.stackoverflow.com/questions/339024/... (Tôi liên tục phớt lờ quyết định đồng thuận, làm những gì là hợp lý, và sau đó gặp rắc rối từ các mods :))
Fattie

2

Swift 4 sử dụng NSLayoutConstraint:

footerBoardImageView.translatesAutoresizingMaskIntoConstraints = false
let widthConstraint  = NSLayoutConstraint(item: yourview, attribute: NSLayoutAttribute.width, relatedBy: NSLayoutRelation.equal, toItem: superview, attribute: NSLayoutAttribute.width, multiplier: 1, constant: 0)
let heightConstraint = NSLayoutConstraint(item: yourview, attribute: NSLayoutAttribute.height, relatedBy: NSLayoutRelation.equal, toItem: superview, attribute: NSLayoutAttribute.height, multiplier: 1, constant: 0)
superview.addConstraints([widthConstraint, heightConstraint])

1

Như một câu trả lời bổ sung và một câu trả lời cho những người không phản đối việc bao gồm các thư viện của bên thứ ba, thư viện PureLayout cung cấp một phương pháp để thực hiện điều này. Khi thư viện được cài đặt, nó đơn giản như

myView.autoPinEdgesToSuperviewEdges()

Có những thư viện khác có thể cung cấp các chức năng tương tự tùy theo sở thích, ví dụ. Masonry , Bản đồ học .


0

Tiếp theo là giải pháp của @ Dschee, đây là cú pháp nhanh chóng 3.0: (Xin lưu ý: đây không phảigiải pháp của tôi , tôi vừa sửa nó cho Swift 3.0)

extension UIView {

    /// Adds constraints to this `UIView` instances `superview` object to make sure this always has the same size as the superview.
    /// Please note that this has no effect if its `superview` is `nil` – add this `UIView` instance as a subview before calling this.
    func bindFrameToSuperviewBounds() {
        guard let superview = self.superview else {
            print("Error! `superview` was nil – call `addSubview(view: UIView)` before calling `bindFrameToSuperviewBounds()` to fix this.")
            return
        }

        self.translatesAutoresizingMaskIntoConstraints = false
        superview.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|-0-[subview]-0-|", options: .directionLeadingToTrailing, metrics: nil, views: ["subview": self]))
    superview.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|-0-[subview]-0-|", options: .directionLeadingToTrailing, metrics: nil, views: ["subview": self]))
}

1
Nếu bạn chỉ thay đổi mã để làm cho nó tuân thủ Swift 3, bạn nên chỉnh sửa câu trả lời áp phích ban đầu thay vì đăng câu trả lời mới (vì đây không phải là thay đổi ý định của áp phích ban đầu). Nếu bạn không đủ điểm để chỉnh sửa, hãy bình luận về bài đăng gốc kèm theo gợi ý về những thay đổi cần thiết để tuân thủ Swift 3. Người đăng ban đầu (hoặc người khác nhìn thấy nhận xét của bạn) sau đó có thể sẽ cập nhật câu trả lời. Bằng cách này, chúng tôi giữ cho chủ đề sạch sẽ khỏi các câu trả lời trùng lặp và mã không dùng nữa.
Jeehut

Tôi hoàn toàn đồng ý với bạn, @Dschee, nhưng có (với tôi, vô lý) nhận xét trên Meta rằng "chúng tôi không làm điều đó trên SO" ... meta.stackoverflow.com/questions/339024/…
Fattie

@JoeBlow Sau khi đọc cuộc thảo luận đằng sau liên kết của bạn, tôi thực sự nghĩ rằng điều này cũng có ý nghĩa. Tôi đồng ý với nhận xét của PatrickHaugh (về câu hỏi) mặc dù nên đưa ra một câu trả lời mới kết hợp với một nhận xét về câu trả lời ban đầu. Sau đó, người đăng ban đầu có cập nhật câu trả lời của mình (để nhận được sự ủng hộ trong tương lai) hay không là tùy thuộc vào người đăng ban đầu. Cảm ơn vi đương link!
Jeehut

Hừ, được rồi, đây là một phần lớn lý do tại sao tôi vẫn là một hit và á quân trung thành trong quá khứ. Tôi lấy câu trả lời của mình, chuyển đổi nó thành cú pháp hiện tại và tiếp tục mã hóa của mình. Lý do chính mà tôi đăng theo cách này là khi tôi dạy swift, tôi thường được hỏi về cách tìm ra giải pháp trong bất kỳ phiên bản nào của swift hiện tại vì các lập trình viên mới gặp khó khăn khi cập nhật các khai báo hàm của họ. Đó là một trong những nguồn thất vọng hàng đầu nhưng cũng là cơ hội để đối lập hai phong cách mã. Kết quả là sinh viên có thể dự đoán những thay đổi tương tự trong các đoạn mã khác.
James Larcombe

0

Tôi cần phải che toàn bộ siêu cảnh. Những cái khác sẽ không làm điều đó trong khi thay đổi định hướng. Vì vậy, tôi đã viết một cái mới - sử dụng hệ số kích thước tùy ý là 20. Hãy thay đổi theo nhu cầu của bạn. Cũng lưu ý rằng điều này trên thực tế làm cho chế độ xem phụ lớn hơn rất nhiều so với chế độ xem siêu cấp có thể khác với yêu cầu.

extension UIView {
    func coverSuperview() {
        guard let superview = self.superview else {
            assert(false, "Error! `superview` was nil – call `addSubview(_ view: UIView)` before calling `\(#function)` to fix this.")
            return
        }
        self.translatesAutoresizingMaskIntoConstraints = false
        let multiplier = CGFloat(20.0)
        NSLayoutConstraint.activate([
            self.heightAnchor.constraint(equalTo: superview.heightAnchor, multiplier: multiplier),
            self.widthAnchor.constraint(equalTo: superview.widthAnchor, multiplier: multiplier),
            self.centerXAnchor.constraint(equalTo: superview.centerXAnchor),
            self.centerYAnchor.constraint(equalTo: superview.centerYAnchor),
            ])
    }
}

0

Tôi đã chọn các yếu tố tốt nhất từ ​​các câu trả lời khác:

extension UIView {
  /// Adds constraints to this `UIView` instances `superview` object to make sure this always has the same size as the superview.
  /// Please note that this has no effect if its `superview` is `nil` – add this `UIView` instance as a subview before calling this.
  func bindFrameToSuperviewBounds() {
    guard let superview = self.superview else {
      print("Error! `superview` was nil – call `addSubview(view: UIView)` before calling `bindFrameToSuperviewBounds()` to fix this.")
      return
    }

    self.translatesAutoresizingMaskIntoConstraints = false

    NSLayoutConstraint.activate([
      self.topAnchor.constraint(equalTo: superview.topAnchor),
      self.bottomAnchor.constraint(equalTo: superview.bottomAnchor),
      self.leadingAnchor.constraint(equalTo: superview.leadingAnchor),
      self.trailingAnchor.constraint(equalTo: superview.trailingAnchor)
    ])
  }
}

Bạn có thể sử dụng nó như thế này, chẳng hạn như trong UIView tùy chỉnh của bạn:

let myView = UIView()
myView.backgroundColor = UIColor.red

self.addSubview(myView)
myView.bindFrameToSuperviewBounds()
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.