我有一个简单的水平UIStackView
,里面堆叠了几个UIView。我的目标是在视图之间创建可变间距。我很清楚我可以使用“spacing”属性在子视图之间创建恒定的空间。但是我的目标是创建可变空间。请注意,如果可能的话,我希望避免使用充当间隔物的隐形视图。
我想出的最好的方法是将UIViews
包裹在单独的UIStackView
中,并使用layoutMarginsRelativeArrangement = YES
来尊重内部堆栈的布局边距。我希望我可以做任何与UIView
类似的事情而不诉诸这种丑陋的工作。这是我的示例代码:
// Create stack view
UIStackView *stackView = [[UIStackView alloc] init];
stackView.translatesAutoresizingMaskIntoConstraints = NO;
stackView.axis = UILayoutConstraintAxisHorizontal;
stackView.alignment = UIStackViewAlignmentCenter;
stackView.layoutMarginsRelativeArrangement = YES;
// Create subview
UIView *view1 = [[UIView alloc] init];
view1.translatesAutoresizingMaskIntoConstraints = NO;
// ... Add Auto Layout constraints for height / width
// ...
// I was hoping the layoutMargins would be respected, but they are not
view1.layoutMargins = UIEdgeInsetsMake(0, 25, 0, 0);
// ... Create more subviews
// UIView view2 = [[UIView alloc] init];
// ...
// Stack the subviews
[stackView addArrangedSubview:view1];
[stackView addArrangedSubview:view2];
结果是一个堆栈,其视图彼此相邻,间距为:
答案 0 :(得分:86)
更新iOS 11,具有自定义间距的StackViews
Apple已添加了在iOS 11中设置自定义间距的功能。您只需在每个排列的子视图后指定间距。不幸的是,您之前无法指定间距。
stackView.setCustomSpacing(10.0, after: firstLabel)
stackView.setCustomSpacing(10.0, after: secondLabel)
仍然比使用自己的观点更好。
适用于iOS 10及以下
您只需在堆栈视图中添加透明视图,并为其添加宽度约束。
(标签 - UIView - 标签 - UIView -Label)
如果您要保留distribution
,那么您可以在UIViews上设置可变宽度约束。
但是如果是这种情况,我会考虑使用stackviews是否是正确的情况。 Autolayout使得在视图之间设置可变宽度变得非常容易。
答案 1 :(得分:2)
SWIFT 4
在lilpit答案之后,这是UIStackView的扩展,可为您的splittedSubview添加顶部和底部间距
extension UIStackView {
func addCustomSpacing(top: CGFloat, bottom: CGFloat) {
//If the stack view has just one arrangedView, we add a dummy one
if self.arrangedSubviews.count == 1 {
self.insertArrangedSubview(UIView(frame: .zero), at: 0)
}
//Getting the second last arrangedSubview and the current one
let lastTwoArrangedSubviews = Array(self.arrangedSubviews.suffix(2))
let arrSpacing: [CGFloat] = [top, bottom]
//Looping through the two last arrangedSubview to add spacing in each of them
for (index, anArrangedSubview) in lastTwoArrangedSubviews.enumerated() {
//After iOS 11, the stackview has a native method
if #available(iOS 11.0, *) {
self.setCustomSpacing(arrSpacing[index], after: anArrangedSubview)
//Before iOS 11 : Adding dummy separator UIViews
} else {
guard let arrangedSubviewIndex = arrangedSubviews.firstIndex(of: anArrangedSubview) else {
return
}
let separatorView = UIView(frame: .zero)
separatorView.translatesAutoresizingMaskIntoConstraints = false
//calculate spacing to keep a coherent spacing with the ios11 version
let isBetweenExisitingViews = arrangedSubviewIndex != arrangedSubviews.count - 1
let existingSpacing = isBetweenExisitingViews ? 2 * spacing : spacing
let separatorSize = arrSpacing[index] - existingSpacing
guard separatorSize > 0 else {
return
}
switch axis {
case .horizontal:
separatorView.widthAnchor.constraint(equalToConstant: separatorSize).isActive = true
case .vertical:
separatorView.heightAnchor.constraint(equalToConstant: separatorSize).isActive = true
}
insertArrangedSubview(separatorView, at: arrangedSubviewIndex + 1)
}
}
}
}
然后,您将像这样使用它:
//Creating label to add to the UIStackview
let label = UILabel(frame: .zero)
//Adding label to the UIStackview
stackView.addArrangedSubview(label)
//Create margin on top and bottom of the UILabel
stackView.addCustomSpacing(top: 40, bottom: 100)
答案 2 :(得分:1)
在Rob's响应中,我创建了一个 UIStackView 扩展名,该扩展名可能会有所帮助:
extension UIStackView {
func addCustomSpacing(_ spacing: CGFloat, after arrangedSubview: UIView) {
if #available(iOS 11.0, *) {
self.setCustomSpacing(spacing, after: arrangedSubview)
} else {
let separatorView = UIView(frame: .zero)
separatorView.translatesAutoresizingMaskIntoConstraints = false
switch axis {
case .horizontal:
separatorView.widthAnchor.constraint(equalToConstant: spacing).isActive = true
case .vertical:
separatorView.heightAnchor.constraint(equalToConstant: spacing).isActive = true
}
if let index = self.arrangedSubviews.firstIndex(of: arrangedSubview) {
insertArrangedSubview(separatorView, at: index + 1)
}
}
}
}
您可以根据需要使用和修改它,例如,如果需要“ separatorView ”引用,则只需返回UIView:
func addCustomSpacing(_ spacing: CGFloat, after arrangedSubview: UIView) -> UIView?
答案 3 :(得分:0)
为了支持iOS 11.x和更低版本,我像提到的Enrique一样扩展了UIStackView,但是我对其进行了修改,使其包括:
extension UIStackView {
func addSpacing(_ spacing: CGFloat, after arrangedSubview: UIView) {
if #available(iOS 11.0, *) {
setCustomSpacing(spacing, after: arrangedSubview)
} else {
let index = arrangedSubviews.firstIndex(of: arrangedSubview)
if let index = index, arrangedSubviews.count > (index + 1), arrangedSubviews[index + 1].accessibilityIdentifier == "spacer" {
arrangedSubviews[index + 1].updateConstraint(axis == .horizontal ? .width : .height, to: spacing)
} else {
let separatorView = UIView(frame: .zero)
separatorView.accessibilityIdentifier = "spacer"
separatorView.translatesAutoresizingMaskIntoConstraints = false
switch axis {
case .horizontal:
separatorView.widthAnchor.constraint(equalToConstant: spacing).isActive = true
case .vertical:
separatorView.heightAnchor.constraint(equalToConstant: spacing).isActive = true
@unknown default:
return
}
if let index = index {
insertArrangedSubview(separatorView, at: index + 1)
}
}
}
}
func addSpacing(_ spacing: CGFloat, before arrangedSubview: UIView) {
let index = arrangedSubviews.firstIndex(of: arrangedSubview)
if let index = index, index > 0, arrangedSubviews[index - 1].accessibilityIdentifier == "spacer" {
let previousSpacer = arrangedSubviews[index - 1]
switch axis {
case .horizontal:
previousSpacer.updateConstraint(.width, to: spacing)
case .vertical:
previousSpacer.updateConstraint(.height, to: spacing)
@unknown default: return // Incase NSLayoutConstraint.Axis is extended in future
}
} else {
let separatorView = UIView(frame: .zero)
separatorView.accessibilityIdentifier = "spacer"
separatorView.translatesAutoresizingMaskIntoConstraints = false
switch axis {
case .horizontal:
separatorView.widthAnchor.constraint(equalToConstant: spacing).isActive = true
case .vertical:
separatorView.heightAnchor.constraint(equalToConstant: spacing).isActive = true
@unknown default:
return
}
if let index = index {
insertArrangedSubview(separatorView, at: max(index - 1, 0))
}
}
}
func removeSpacing(after arrangedSubview: UIView) {
if #available(iOS 11.0, *) {
setCustomSpacing(0, after: arrangedSubview)
} else {
if let index = arrangedSubviews.firstIndex(of: arrangedSubview), arrangedSubviews.count > (index + 1), arrangedSubviews[index + 1].accessibilityIdentifier == "spacer" {
arrangedSubviews[index + 1].removeFromStack()
}
}
}
func removeSpacing(before arrangedSubview: UIView) {
if let index = arrangedSubviews.firstIndex(of: arrangedSubview), index > 0, arrangedSubviews[index - 1].accessibilityIdentifier == "spacer" {
arrangedSubviews[index - 1].removeFromStack()
}
}
}
extension UIView {
func updateConstraint(_ attribute: NSLayoutConstraint.Attribute, to constant: CGFloat) {
for constraint in constraints {
if constraint.firstAttribute == attribute {
constraint.constant = constant
}
}
}
func removeFromStack() {
if let stack = superview as? UIStackView, stack.arrangedSubviews.contains(self) {
stack.removeArrangedSubview(self)
// Note: 1
removeFromSuperview()
}
}
}
注意:1-根据文档:
为防止视图在调用堆栈的视图后出现在屏幕上 removeArrangedSubview:方法,从中显式删除视图 通过调用视图的removeFromSuperview()方法来实现subviews数组,或者 将视图的isHidden属性设置为true。
答案 4 :(得分:0)
要实现类似CSS边距和填充的行为。
填充
myStackView.directionalLayoutMargins = NSDirectionalEdgeInsets(top:top, 前导:左侧,底部:底部,尾部:右侧);
边距(创建包装视图,并向包装中添加填充)
wrapper = UIStackView();
wrapper!.frame = viewToAdd.frame;
wrapper!.frame.size.height = wrapper!.frame.size.height + marginTop + marginBottom;
wrapper!.frame.size.width = wrapper!.frame.size.width + marginLeft + marginRight;
(wrapper! as! UIStackView).axis = .horizontal;
(wrapper! as! UIStackView).alignment = .fill
(wrapper! as! UIStackView).spacing = 0
(wrapper! as! UIStackView).distribution = .fill
wrapper!.translatesAutoresizingMaskIntoConstraints = false
(wrapper! as! UIStackView).isLayoutMarginsRelativeArrangement = true;
(wrapper! as! UIStackView).insetsLayoutMarginsFromSafeArea = false;
wrapper!.directionalLayoutMargins = NSDirectionalEdgeInsets(top: marginTop, leading: marginLeft, bottom: marginBottom, trailing: marginRight);wrapper.addArrangedSubview(viewToAdd);
答案 5 :(得分:0)
如果您不知道以前的视图,则可以创建自己的间距UIView并将其作为已安排的子视图添加到堆栈视图中。
func spacing(value: CGFloat) -> UIView {
let spacerView = UIView(frame: CGRect(x: 0, y: 0, width: 0, height: 0))
spacerView.translatesAutoresizingMaskIntoConstraints = false
spacerView.heightAnchor.constraint(equalToConstant: value).isActive = true
return spacerView
}
stackView.addArrangedSubview(spacing(value: 16))
答案 6 :(得分:-1)
我创建了一个库LRCustomSpacingStackView,以支持iOS 9及更高版本上的UIStackView自定义间距。
只需使用此库的扩展方法替换代码中UIStackView的内置方法,并通过设置任何子视图的lr_stackSpacing
属性来指定间距:
import LRCustomSpacingStackView
stackView.lr_addArrangedSubview(view1)
view1.lr_stackSpacing = UIEdgeInsets(top: 20, left: 30, bottom: 10, right: 0)
所有UIStackView的方法都有其替代:
lr_arrangedSubviews
for arrangedSubviews
lr_addArrangedSubview(_:)
for addArrangedSubview(_:)
lr_removeArrangedSubview(_:)
for removeArrangedSubview(_:)
lr_insertArrangedSubview(_:at:)
for insertArrangedSubview(_:at:)
lr_stackSpacing
比setCustomSpacing(_:after:)
更灵活,功能更强大。您可以在4个方向上指定视图的间距:顶部,右侧,底部,左侧。