宽度和高度等于它的超视图使用自动布局编程?

我一直在网上寻找很多片段,但仍然找不到问题的答案。我的问题是,我有一个 scrollView (SV) ,我想在 scrollView (SV)里面添加一个按钮,它的总视图的宽度和高度都是 scrollView (SV) ,这样当用户旋转设备按钮时,就会有相同的 scrollView (SV)帧。如何做 NSLayout/NSLayoutConstraint?谢谢

93754 次浏览

This link can help you,follow the instructions : http://www.raywenderlich.com/20881/beginning-auto-layout-part-1-of-2

EDIT :

use following code snippet, where subview is your subivew.

[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)]];

I'm not sure if this is the most efficient way to do it, but it works..

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];

If someone is looking for a Swift solution – I would create a Swift extension for UIView which will help you each time you want to bind a subviews frame to its superviews bounds:

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


}
}

Then simply call it like this:

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

As a supplemental answer, and one for those not opposed to including third party libraries, the PureLayout library provides a method to do just this. Once the library is installed, it's as simple as

myView.autoPinEdgesToSuperviewEdges()

There are other libraries which can provide similar functionality as well depending on taste, eg. Masonry, Cartography.

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]))
}


}

As a follow up to @Dschee's solution, here is swift 3.0 syntax: (Please note: this is not my solution, I have just fixed it for 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]))
}

Approach #1: Via UIView Extension

Here's a more functional approach in Swift 3+ with a precondition instead of a print (which can perish easily in the console). This one will report programmer errors as failed builds.

Add this extension to your project:

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]))
}
}
}

Now simply call it like this:

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

Note that the above method is already integrated into my HandyUIKit framework which also adds some more handy UI helpers into your project.


Approach #2: Using a Framework

If you work a lot with programmatic constraints in your project then I recommend you to checkout SnapKit. It makes working with constraints a lot easier and less error-prone.

Follow the installation instructions in the docs to include SnapKit into your project. Then import it at the top of your Swift file:

import SnapKit

Now you can achieve the same thing with just this:

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

addConstraint and removeConstraint methods for UIView are going to be deprecated, so it's worth to use 'constraint creation conveniences':

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

Swift 4 using 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])

I needed to cover the superview completely. The other ones wouldn't do that during orientation changes. So I wrote a new one which does - using an arbitrary size multiplier of 20. Feel free to change to your needs. Also note this one in fact makes the subview a lot bigger than the superview which might be different from requirements.

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),
])
}
}

I've picked the best elements from the other answers:

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)
])
}
}

You can use it like this, for example in your custom UIView:

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


self.addSubview(myView)
myView.bindFrameToSuperviewBounds()