自动布局让我的生活很困难。从理论上讲,当我转换的时候,它会非常有用,但我似乎一直在与它作斗争。
我做了一个演示项目来寻求帮助。有人知道如何使视图之间的空间增加或减少均匀,每当视图调整大小?
下面是三个标签(手动垂直对齐):
我想要的是,当我旋转时,它们均匀地调整间距(而不是视图大小)。默认情况下,顶部和底部视图向中心挤压:
自动布局让我的生活很困难。从理论上讲,当我转换的时候,它会非常有用,但我似乎一直在与它作斗争。
我做了一个演示项目来寻求帮助。有人知道如何使视图之间的空间增加或减少均匀,每当视图调整大小?
下面是三个标签(手动垂直对齐):
我想要的是,当我旋转时,它们均匀地调整间距(而不是视图大小)。默认情况下,顶部和底部视图向中心挤压:
当前回答
Swift 3版本
let _redView = UIView()
_redView.backgroundColor = UIColor.red
_redView.translatesAutoresizingMaskIntoConstraints = false
let _yellowView = UIView()
_yellowView.backgroundColor = UIColor.yellow
_yellowView.translatesAutoresizingMaskIntoConstraints = false
let _blueView = UIView()
_blueView.backgroundColor = UIColor.blue
_blueView.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(_redView)
self.view.addSubview(_yellowView)
self.view.addSubview(_blueView)
var views = ["_redView": _redView, "_yellowView": _yellowView, "_blueView":_blueView]
var nslayoutConstraint_H = NSLayoutConstraint.constraints(withVisualFormat: "|->=0-[_redView(40)]->=0-[_yellowView(40)]->=0-[_blueView(40)]->=0-|", options: [.alignAllTop, .alignAllBottom], metrics: nil, views: views)
self.view.addConstraints(nslayoutConstraint_H)
var nslayoutConstraint_V = NSLayoutConstraint.constraints(withVisualFormat: "V:[_redView(60)]", options: NSLayoutFormatOptions.init(rawValue: 0), metrics: nil, views: views)
self.view.addConstraints(nslayoutConstraint_V)
let constraint_red = NSLayoutConstraint.init(item: self.view, attribute: .centerY, relatedBy: .equal, toItem: _redView, attribute: .centerY, multiplier: 1, constant: 0)
self.view.addConstraint(constraint_red)
let constraint_yellow = NSLayoutConstraint.init(item: self.view, attribute: .centerX, relatedBy: .equal, toItem: _yellowView, attribute: .centerX, multiplier: 1, constant: 0)
self.view.addConstraint(constraint_yellow)
let constraint_yellow1 = NSLayoutConstraint.init(item: _redView, attribute: .centerX, relatedBy: .equal, toItem: _yellowView, attribute: .leading, multiplier: 0.5, constant: 0)
self.view.addConstraint(constraint_yellow1)
let constraint_yellow2 = NSLayoutConstraint.init(item: _blueView, attribute: .centerX, relatedBy: .equal, toItem: _yellowView, attribute: .leading, multiplier: 1.5, constant: 40)
self.view.addConstraint(constraint_yellow2)
其他回答
我刚刚用乘数特性解决了我的问题。我不确定它是否适用于所有情况,但对我来说,它非常有效。我在Xcode 6.3供你参考。
我最后做的是:
1)首先将我的按钮放置在320px宽度的屏幕上,以我希望它在320px设备上的方式分布。
2)然后我在所有按钮的superview上添加了一个领先的空间约束。
3)然后我修改了前导空间的属性,使常量为0,乘数是x偏移量除以屏幕宽度(例如,我的第一个按钮距离左边缘8px,所以我将乘数设置为8/320)
4)那么这里重要的一步是将约束关系中的第二个Item更改为superview。尾随而不是superview.leading。这很关键,因为superview。在我的例子中,前导为0,后拖为320,所以8/320在320px设备上是8px,然后当父视图的宽度改变为640或其他什么时,视图都以相对于320px屏幕大小的宽度的比例移动。这里的数学比较容易理解。
在InterfaceBuilder中解决这个问题非常简单:
设置居中标签(label2)为“水平容器中心”和“垂直容器中心”
选择居中标签和顶部标签(label1 + label2),并为垂直间距添加两个约束。大于或等于最小间距的一个。小于或等于最大间距的一个。
中间的标签和底部的标签(label2 + label3)也是如此。
此外,您还可以添加两个约束label1 -顶部空间到SuperView和两个约束label2 -底部空间到SuperView。
结果是所有4个空格的大小变化相同。
基于Ben Dolman的回答,这将更均匀地分配视图(使用填充等):
+(NSArray *)constraintsForEvenDistributionOfItems:(NSArray *)views
relativeToCenterOfItem:(id)toView vertically:(BOOL)vertically
{
NSMutableArray *constraints = [NSMutableArray new];
NSLayoutAttribute attr = vertically ? NSLayoutAttributeCenterY : NSLayoutAttributeCenterX;
CGFloat min = 0.25;
CGFloat max = 1.75;
CGFloat d = (max-min) / ([views count] - 1);
for (NSUInteger i = 0; i < [views count]; i++) {
id view = views[i];
CGFloat multiplier = i * d + min;
NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:view
attribute:attr
relatedBy:NSLayoutRelationEqual
toItem:toView
attribute:attr
multiplier:multiplier
constant:0];
[constraints addObject:constraint];
}
return constraints;
}
Android has a method of chaining views together in its constraint based layout system that I wanted to mimic. Searches brought me here but none of the answers quite worked. I didn't want to use StackViews because they tend to cause me more grief down the line than they save up front. I ended up creating a solution that used UILayoutGuides placed between the views. Controlling their width's allows different types of distributions, chain styles in Android parlance. The function accepts a leading and trailing anchor instead of a parent view. This allows the chain to be placed between two arbitrary views rather than distributed inside of the parent view. It does use UILayoutGuide which is only available in iOS 9+ but that shouldn't be a problem anymore.
public enum LayoutConstraintChainStyle {
case spread //Evenly distribute between the anchors
case spreadInside //Pin the first & last views to the sides and then evenly distribute
case packed //The views have a set space but are centered between the anchors.
}
public extension NSLayoutConstraint {
static func chainHorizontally(views: [UIView],
leadingAnchor: NSLayoutXAxisAnchor,
trailingAnchor: NSLayoutXAxisAnchor,
spacing: CGFloat = 0.0,
style: LayoutConstraintChainStyle = .spread) -> [NSLayoutConstraint] {
var constraints = [NSLayoutConstraint]()
guard views.count > 1 else { return constraints }
guard let first = views.first, let last = views.last, let superview = first.superview else { return constraints }
//Setup the chain of views
var distributionGuides = [UILayoutGuide]()
var previous = first
let firstGuide = UILayoutGuide()
superview.addLayoutGuide(firstGuide)
distributionGuides.append(firstGuide)
firstGuide.identifier = "ChainDistribution\(distributionGuides.count)"
constraints.append(firstGuide.leadingAnchor.constraint(equalTo: leadingAnchor))
constraints.append(first.leadingAnchor.constraint(equalTo: firstGuide.trailingAnchor, constant: spacing))
views.dropFirst().forEach { view in
let g = UILayoutGuide()
superview.addLayoutGuide(g)
distributionGuides.append(g)
g.identifier = "ChainDistribution\(distributionGuides.count)"
constraints.append(contentsOf: [
g.leadingAnchor.constraint(equalTo: previous.trailingAnchor),
view.leadingAnchor.constraint(equalTo: g.trailingAnchor)
])
previous = view
}
let lastGuide = UILayoutGuide()
superview.addLayoutGuide(lastGuide)
constraints.append(contentsOf: [lastGuide.leadingAnchor.constraint(equalTo: last.trailingAnchor),
lastGuide.trailingAnchor.constraint(equalTo: trailingAnchor)])
distributionGuides.append(lastGuide)
//Space the according to the style.
switch style {
case .packed:
if let first = distributionGuides.first, let last = distributionGuides.last {
constraints.append(first.widthAnchor.constraint(greaterThanOrEqualToConstant: spacing))
constraints.append(last.widthAnchor.constraint(greaterThanOrEqualToConstant: spacing))
constraints.append(last.widthAnchor.constraint(equalTo: first.widthAnchor))
constraints.append(contentsOf:
distributionGuides.dropFirst().dropLast()
.map { $0.widthAnchor.constraint(equalToConstant: spacing) }
)
}
case .spread:
if let first = distributionGuides.first {
constraints.append(contentsOf:
distributionGuides.dropFirst().map { $0.widthAnchor.constraint(equalTo: first.widthAnchor) })
}
case .spreadInside:
if let first = distributionGuides.first, let last = distributionGuides.last {
constraints.append(first.widthAnchor.constraint(equalToConstant: spacing))
constraints.append(last.widthAnchor.constraint(equalToConstant: spacing))
let innerGuides = distributionGuides.dropFirst().dropLast()
if let key = innerGuides.first {
constraints.append(contentsOf:
innerGuides.dropFirst().map { $0.widthAnchor.constraint(equalTo: key.widthAnchor) }
)
}
}
}
return constraints
}
在iOS 9中,苹果通过(期待已久的)UIStackView使这变得非常容易。只需选择要包含在接口构建器中的视图,并选择编辑器->嵌入->堆栈视图。为堆栈视图设置适当的宽度/高度/边距约束,并确保将Distribution属性设置为'Equal spacing':
当然,如果你需要支持iOS 8或更低版本,你必须从其他选项中选择一个。