以编程方式在UISTackView中添加视图

我正在尝试以编程方式在UISTackView中添加视图。 现在我的代码是:

UIView *view1 = [[UIView alloc]init];
view1.backgroundColor = [UIColor blackColor];
[view1 setFrame:CGRectMake(0, 0, 100, 100)];


UIView *view2 =  [[UIView alloc]init];
view2.backgroundColor = [UIColor greenColor];
[view2 setFrame:CGRectMake(0, 100, 100, 100)];


[self.stack1 addArrangedSubview:view1];
[self.stack1 addArrangedSubview:view2];

当我部署应用程序时,只有一个视图,而且是黑色的。(视图1也获取视图2的参数)

290548 次浏览

你必须设置你的分布类型。 在代码中,只需添加:

self.stack1.distribution = UIStackViewDistributionFillEqually;

或者,您可以直接在Interface Builder中设置分布。 例如:

enter image description here

希望对您有所帮助;) 拉皮努.

尝试以下代码:

UIView *view1 = [[UIView alloc]init];
view1.backgroundColor = [UIColor blackColor];
[view1 setFrame:CGRectMake(0, 0, 50, 50)];


UIView *view2 =  [[UIView alloc]init];
view2.backgroundColor = [UIColor greenColor];
[view2 setFrame:CGRectMake(0, 100, 100, 100)];


NSArray *subView = [NSArray arrayWithObjects:view1,view2, nil];


[self.stack1 initWithArrangedSubviews:subView];

堆栈视图使用内部内容大小,因此使用布局约束来定义视图的尺寸。

有一种简单的方法可以快速添加约束(示例):

[view1.heightAnchor constraintEqualToConstant:100].active = true;

完整代码:

- (void) setup {


//View 1
UIView *view1 = [[UIView alloc] init];
view1.backgroundColor = [UIColor blueColor];
[view1.heightAnchor constraintEqualToConstant:100].active = true;
[view1.widthAnchor constraintEqualToConstant:120].active = true;




//View 2
UIView *view2 = [[UIView alloc] init];
view2.backgroundColor = [UIColor greenColor];
[view2.heightAnchor constraintEqualToConstant:100].active = true;
[view2.widthAnchor constraintEqualToConstant:70].active = true;


//View 3
UIView *view3 = [[UIView alloc] init];
view3.backgroundColor = [UIColor magentaColor];
[view3.heightAnchor constraintEqualToConstant:100].active = true;
[view3.widthAnchor constraintEqualToConstant:180].active = true;


//Stack View
UIStackView *stackView = [[UIStackView alloc] init];


stackView.axis = UILayoutConstraintAxisVertical;
stackView.distribution = UIStackViewDistributionEqualSpacing;
stackView.alignment = UIStackViewAlignmentCenter;
stackView.spacing = 30;




[stackView addArrangedSubview:view1];
[stackView addArrangedSubview:view2];
[stackView addArrangedSubview:view3];


stackView.translatesAutoresizingMaskIntoConstraints = false;
[self.view addSubview:stackView];




//Layout for Stack View
[stackView.centerXAnchor constraintEqualToAnchor:self.view.centerXAnchor].active = true;
[stackView.centerYAnchor constraintEqualToAnchor:self.view.centerYAnchor].active = true;
}

注意:这是在IOS 9上测试的

UIStackView Equal Spacing (centered)

Swift 5.0

//Image View
let imageView = UIImageView()
imageView.backgroundColor = UIColor.blue
imageView.heightAnchor.constraint(equalToConstant: 120.0).isActive = true
imageView.widthAnchor.constraint(equalToConstant: 120.0).isActive = true
imageView.image = UIImage(named: "buttonFollowCheckGreen")


//Text Label
let textLabel = UILabel()
textLabel.backgroundColor = UIColor.yellow
textLabel.widthAnchor.constraint(equalToConstant: self.view.frame.width).isActive = true
textLabel.heightAnchor.constraint(equalToConstant: 20.0).isActive = true
textLabel.text  = "Hi World"
textLabel.textAlignment = .center


//Stack View
let stackView   = UIStackView()
stackView.axis  = NSLayoutConstraint.Axis.vertical
stackView.distribution  = UIStackView.Distribution.equalSpacing
stackView.alignment = UIStackView.Alignment.center
stackView.spacing   = 16.0


stackView.addArrangedSubview(imageView)
stackView.addArrangedSubview(textLabel)
stackView.translatesAutoresizingMaskIntoConstraints = false


self.view.addSubview(stackView)


//Constraints
stackView.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true
stackView.centerYAnchor.constraint(equalTo: self.view.centerYAnchor).isActive = true

基于@user1046037的答案。

    //Image View
let imageView               = UIImageView()
imageView.backgroundColor   = UIColor.blueColor()
imageView.heightAnchor.constraintEqualToConstant(120.0).active = true
imageView.widthAnchor.constraintEqualToConstant(120.0).active = true
imageView.image = UIImage(named: "buttonFollowCheckGreen")


//Text Label
let textLabel               = UILabel()
textLabel.backgroundColor   = UIColor.greenColor()
textLabel.widthAnchor.constraintEqualToConstant(self.view.frame.width).active = true
textLabel.heightAnchor.constraintEqualToConstant(20.0).active = true
textLabel.text  = "Hi World"
textLabel.textAlignment = .Center




//Third View
let thirdView               = UIImageView()
thirdView.backgroundColor   = UIColor.magentaColor()
thirdView.heightAnchor.constraintEqualToConstant(120.0).active = true
thirdView.widthAnchor.constraintEqualToConstant(120.0).active = true
thirdView.image = UIImage(named: "buttonFollowMagenta")




//Stack View
let stackView   = UIStackView()
stackView.axis  = UILayoutConstraintAxis.Vertical
stackView.distribution  = UIStackViewDistribution.EqualSpacing
stackView.alignment = UIStackViewAlignment.Center
stackView.spacing   = 16.0


stackView.addArrangedSubview(imageView)
stackView.addArrangedSubview(textLabel)
stackView.addArrangedSubview(thirdView)
stackView.translatesAutoresizingMaskIntoConstraints = false;


self.view.addSubview(stackView)


//Constraints
stackView.centerXAnchor.constraintEqualToAnchor(self.view.centerXAnchor).active = true
stackView.centerYAnchor.constraintEqualToAnchor(self.view.centerYAnchor).active = true

答案由@Oleg Popov 改进

UIStackView在内部使用约束来定位其排列好的子视图。确切地说,创建哪些约束取决于如何配置堆栈视图本身。默认情况下,堆栈视图将创建约束,将其排列好的子视图放置在水平线上,将前面和后面的视图固定在自己的前面和后面的边缘上。因此,您的代码将生成如下布局:

|[view1][view2]|

分配给每个子视图的空间由许多因素决定,包括子视图的内在内容大小、它的压缩阻力和内容拥抱优先级。默认情况下,UIView实例不定义内部内容大小。这通常是由子类(如 UILabelUIButton)提供的。

由于两个新的 UIView实例的内容压缩阻力和内容拥抱优先级是相同的,而且两个视图都不提供内部内容大小,布局引擎必须对每个视图应该分配什么大小做出最佳猜测。在您的示例中,它将100% 可用空间分配给第一个视图,而不会分配给第二个视图。

如果修改代码使用 UILabel实例,将会得到更好的结果:

UILabel *label1 = [UILabel new];
label1.text = @"Label 1";
label1.backgroundColor = [UIColor blueColor];


UILabel *label2 = [UILabel new];
label2.text = @"Label 2";
label2.backgroundColor = [UIColor greenColor];


[self.stack1 addArrangedSubview:label1];
[self.stack1 addArrangedSubview:label2];

请注意,没有必要自己显式创建任何约束。这是使用 UIStackView的主要好处——它向开发人员隐藏了约束管理的细节(通常很难看)。

以下两行修正了我的问题

view.heightAnchor.constraintEqualToConstant(50).active = true;
view.widthAnchor.constraintEqualToConstant(350).active = true;

快速版

var DynamicView=UIView(frame: CGRectMake(100, 200, 100, 100))
DynamicView.backgroundColor=UIColor.greenColor()
DynamicView.layer.cornerRadius=25
DynamicView.layer.borderWidth=2
self.view.addSubview(DynamicView)
DynamicView.heightAnchor.constraintEqualToConstant(50).active = true;
DynamicView.widthAnchor.constraintEqualToConstant(350).active = true;


var DynamicView2=UIView(frame: CGRectMake(100, 310, 100, 100))
DynamicView2.backgroundColor=UIColor.greenColor()
DynamicView2.layer.cornerRadius=25
DynamicView2.layer.borderWidth=2
self.view.addSubview(DynamicView2)
DynamicView2.heightAnchor.constraintEqualToConstant(50).active = true;
DynamicView2.widthAnchor.constraintEqualToConstant(350).active = true;


var DynamicView3:UIView=UIView(frame: CGRectMake(10, 420, 355, 100))
DynamicView3.backgroundColor=UIColor.greenColor()
DynamicView3.layer.cornerRadius=25
DynamicView3.layer.borderWidth=2
self.view.addSubview(DynamicView3)


let yourLabel:UILabel = UILabel(frame: CGRectMake(110, 10, 200, 20))
yourLabel.textColor = UIColor.whiteColor()
//yourLabel.backgroundColor = UIColor.blackColor()
yourLabel.text = "mylabel text"
DynamicView3.addSubview(yourLabel)
DynamicView3.heightAnchor.constraintEqualToConstant(50).active = true;
DynamicView3.widthAnchor.constraintEqualToConstant(350).active = true;


let stackView   = UIStackView()
stackView.axis  = UILayoutConstraintAxis.Vertical
stackView.distribution  = UIStackViewDistribution.EqualSpacing
stackView.alignment = UIStackViewAlignment.Center
stackView.spacing   = 30


stackView.addArrangedSubview(DynamicView)
stackView.addArrangedSubview(DynamicView2)
stackView.addArrangedSubview(DynamicView3)


stackView.translatesAutoresizingMaskIntoConstraints = false;


self.view.addSubview(stackView)


//Constraints
stackView.centerXAnchor.constraintEqualToAnchor(self.view.centerXAnchor).active = true
stackView.centerYAnchor.constraintEqualToAnchor(self.view.centerYAnchor).active = true

我只是碰到了非常相似的问题。正如前面提到的,堆栈视图的维度取决于排列子视图的一个固有内容大小。以下是我在 Swift 2.x 中的解决方案,视图结构如下:

视图-UIView

CustomView-CustomView: UIView

StackView-UISTackView

排列子视图-自定义 UIView 子类

    //: [Previous](@previous)


import Foundation
import UIKit
import XCPlayground


/**Container for stack view*/
class CustomView:UIView {


override init(frame: CGRect) {
super.init(frame: frame)
}


required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}




init(){
super.init(frame: CGRectZero)


}


}


/**Custom Subclass*/
class CustomDrawing:UIView{
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}


required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()


}


func setup(){
// self.backgroundColor = UIColor.clearColor()
print("setup \(frame)")
}


override func drawRect(rect: CGRect) {
let ctx = UIGraphicsGetCurrentContext()
CGContextMoveToPoint(ctx, 0, 0)
CGContextAddLineToPoint(ctx, CGRectGetWidth(bounds), CGRectGetHeight(bounds))
CGContextStrokePath(ctx)


print("DrawRect")


}
}






//: [Next](@next)
let stackView = UIStackView()
stackView.distribution = .FillProportionally
stackView.alignment = .Center
stackView.axis = .Horizontal
stackView.spacing = 10




//container view
let view = UIView(frame: CGRectMake(0,0,320,640))
view.backgroundColor = UIColor.darkGrayColor()
//now custom view


let customView = CustomView()


view.addSubview(customView)


customView.translatesAutoresizingMaskIntoConstraints = false
customView.widthAnchor.constraintEqualToConstant(220).active = true
customView.heightAnchor.constraintEqualToConstant(60).active = true
customView.centerXAnchor.constraintEqualToAnchor(view.centerXAnchor).active = true
customView.centerYAnchor.constraintEqualToAnchor(view.centerYAnchor).active = true
customView.backgroundColor = UIColor.lightGrayColor()


//add a stack view
customView.addSubview(stackView)
stackView.centerXAnchor.constraintEqualToAnchor(customView.centerXAnchor).active = true
stackView.centerYAnchor.constraintEqualToAnchor(customView.centerYAnchor).active = true
stackView.translatesAutoresizingMaskIntoConstraints = false




let c1 = CustomDrawing()
c1.translatesAutoresizingMaskIntoConstraints = false
c1.backgroundColor = UIColor.redColor()
c1.widthAnchor.constraintEqualToConstant(30).active = true
c1.heightAnchor.constraintEqualToConstant(30).active = true


let c2 = CustomDrawing()
c2.translatesAutoresizingMaskIntoConstraints = false
c2.backgroundColor = UIColor.blueColor()
c2.widthAnchor.constraintEqualToConstant(30).active = true
c2.heightAnchor.constraintEqualToConstant(30).active = true




stackView.addArrangedSubview(c1)
stackView.addArrangedSubview(c2)




XCPlaygroundPage.currentPage.liveView = view

对于尝试在堆栈视图中隐藏任何视图时接受的答案,约束的工作方式不正确。

Unable to simultaneously satisfy constraints.
Probably at least one of the constraints in the following list is one you don't want.
Try this:
(1) look at each constraint and try to figure out which you don't expect;
(2) find the code that added the unwanted constraint or constraints and fix it.
(
"<NSLayoutConstraint:0x618000086e50 UIView:0x7fc11c4051c0.height == 120   (active)>",
"<NSLayoutConstraint:0x610000084fb0 'UISV-hiding' UIView:0x7fc11c4051c0.height == 0   (active)>"
)

原因 是当在 stackView中隐藏 view时,它会将高度设置为0以使其动画化。

解决方案 修改约束 priority,如下所示。

import UIKit


class ViewController: UIViewController {


let stackView = UIStackView()
let a = UIView()
let b = UIView()


override func viewDidLoad() {
super.viewDidLoad()


a.backgroundColor = UIColor.red
a.widthAnchor.constraint(equalToConstant: 200).isActive = true
let aHeight = a.heightAnchor.constraint(equalToConstant: 120)
aHeight.isActive = true
aHeight.priority = 999


let bHeight = b.heightAnchor.constraint(equalToConstant: 120)
bHeight.isActive = true
bHeight.priority = 999
b.backgroundColor = UIColor.green
b.widthAnchor.constraint(equalToConstant: 200).isActive = true


view.addSubview(stackView)
stackView.backgroundColor = UIColor.blue
stackView.addArrangedSubview(a)
stackView.addArrangedSubview(b)
stackView.axis = .vertical
stackView.distribution = .equalSpacing
stackView.translatesAutoresizingMaskIntoConstraints = false
}


override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}


// Just add a button in xib file or storyboard and add connect this action.
@IBAction func test(_ sender: Any) {
a.isHidden = !a.isHidden
}


}

在 Swift 4.2中

let redView = UIView()
redView.backgroundColor = .red


let blueView = UIView()
blueView.backgroundColor = .blue


let stackView = UIStackView(arrangedSubviews: [redView, blueView])
stackView.axis = .vertical
stackView.distribution = .fillEqually


view.addSubview(stackView)


// stackView.frame = CGRect(x: 0, y: 0, width: 200, height: 200)


// autolayout constraint
stackView.translatesAutoresizingMaskIntoConstraints = false


NSLayoutConstraint.activate([
stackView.topAnchor.constraint(equalTo: view.topAnchor),
stackView.leftAnchor.constraint(equalTo: view.leftAnchor),
stackView.rightAnchor.constraint(equalTo: view.rightAnchor),
stackView.heightAnchor.constraint(equalToConstant: 200)
])

对于我来说,最让我头疼的是我忘记了这句话:

stackView.translatesAutoresizingMaskIntoConstraints = false;

在那之后,不需要对我安排的子视图设置任何约束,stackview 会处理这个问题。

Swift 5 版本的 Oleg Popov 的回答,这是基于用户1046037的回答

//Image View
let imageView = UIImageView()
imageView.backgroundColor = UIColor.blue
imageView.heightAnchor.constraint(equalToConstant: 120.0).isActive = true
imageView.widthAnchor.constraint(equalToConstant: 120.0).isActive = true
imageView.image = UIImage(named: "buttonFollowCheckGreen")


//Text Label
let textLabel = UILabel()
textLabel.backgroundColor = UIColor.yellow
textLabel.widthAnchor.constraint(equalToConstant: self.view.frame.width).isActive = true
textLabel.heightAnchor.constraint(equalToConstant: 20.0).isActive = true
textLabel.text  = "Hi World"
textLabel.textAlignment = .center


//Stack View
let stackView   = UIStackView()
stackView.axis  = NSLayoutConstraint.Axis.vertical
stackView.distribution  = UIStackView.Distribution.equalSpacing
stackView.alignment = UIStackView.Alignment.center
stackView.spacing   = 16.0


stackView.addArrangedSubview(imageView)
stackView.addArrangedSubview(textLabel)
stackView.translatesAutoresizingMaskIntoConstraints = false


self.view.addSubview(stackView)


//Constraints
stackView.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true
stackView.centerYAnchor.constraint(equalTo: self.view.centerYAnchor).isActive = true

您可以使用一个子类来以更简单的方式处理 UIView类的 .intrinsicContentSize,而不是编写所有约束。 这个解决方案在某种程度上也改进了 Interface Builder,以支持“ IninsicWidth”和“ 身高”的视图。虽然您可以扩展 UIView 的,并且在 IB中的所有 UIView 上都可以使用这些属性,但是它的清洁器是子类。

// IntrinsicView.h
@import UIKit


IB_DESIGNABLE
@interface IntrinsicView : UIView
-(instancetype)initWithFrame:(CGRect)rect;
@property IBInspectable CGSize intrinsic;
@end
// IntrinsicView.m
#import "IntrinsicView.h"


@implementation IntrinsicView {
CGSize _intrinsic;
}
- (instancetype)initWithFrame:(CGRect)frame {
_intrinsic = frame.size;
if ( !(self = [super initWithFrame:frame]) ) return nil;
// your stuff here..
return self;
}
-(CGSize)intrinsicContentSize {
return _intrinsic;
}
-(void)prepareForInterfaceBuilder {
self.frame = CGRectMake(self.frame.origin.x, self.frame.origin.y, _intrinsic.width,_intrinsic.height);
}
@end

这意味着您可以只分配这些 IntrinsicView 的和 self.frame.size是采取作为 intrinsicContentSize。这样就不会干扰正常的布局,也不需要设置约束关系,这些约束关系甚至不能完全应用于 UIStackViews

#import "IntrinsicView.h"


- (void)viewDidLoad {
[super viewDidLoad];
    

UIStackView *column = [[UIStackView alloc] initWithFrame:self.view.frame];
column.spacing = 2;
column.alignment = UIStackViewAlignmentFill;
column.axis = UILayoutConstraintAxisVertical; //Up-Down
column.distribution = UIStackViewDistributionFillEqually;
    

for (int row=0; row<5; row++) {
//..frame:(CGRect) defines here proportions and
//relation to axis of StackView
IntrinsicView *intrinsicView = [[IntrinsicView alloc] initWithFrame:CGRectMake(0, 0, 30.0, 30.0)];
        

[column addArrangedSubview:intrinsicView];
}
[self.view addSubview:column];
}

现在你可以疯狂的使用 UIStackView enter image description here

或者在 迅速 + 编码,解码,IB 支持,Objective-C 支持

@IBDesignable @objc class IntrinsicView : UIView {
@IBInspectable var intrinsic : CGSize
@objc override init(frame: CGRect) {
intrinsic = frame.size
super.init(frame: frame)
}
required init?(coder: NSCoder) {
intrinsic = coder.decodeCGSize(forKey: "intrinsic")
super.init(coder: coder)
}
override func encode(with coder: NSCoder) {
coder.encode(intrinsic, forKey: "intrinsic")
super.encode(with: coder)
}
override var intrinsicContentSize: CGSize {
return intrinsic
}
override func prepareForInterfaceBuilder() {
frame = CGRect(x: self.frame.origin.x, y: self.frame.origin.y, width: intrinsic.width, height: intrinsic.height)
}
}

真的不建议设置高度限制... 如果你可以,永远,永远,永远不要设置高度!您需要检查 UIStackView 中视图的所有约束,并确保存在底部、顶部、前导和尾部的约束。有人对我说: 这就像一个家伙推墙。如果他不从四面推,其中一面墙就会倒在他身上。

如果要添加许多 UIView,可以使用下面的扩展,在这里传递一个 UIView数组,然后按顺序将它们添加到 UIStackView

extension UIStackView {
func addArrangedSubviews(_ subviews: [UIView]) {
subviews.forEach{ self.addArrangedSubview($0) }
}
}
func configureHorizontalView(){
containerView.addSubview(horizontalStackView)
_ = horizontalStackView.anchor(top: secondCurrencyTextField.bottomAnchor,
left: containerView.leftAnchor,
bottom: nil,
right: containerView.rightAnchor,
topConstant: 40,
leftConstant: 30,
bottomConstant: 0,
rightConstant: 30,
widthConstant: 0,
heightConstant: 65)
}


func configureFirstDropDownlabel(){
//add a view to stackView with addArrangedSubview()
horizontalStackView.addArrangedSubview(firstDropDownlabel)
_ = firstDropDownlabel.anchor(top: horizontalStackView.bottomAnchor,
left: horizontalStackView.leftAnchor,
bottom: nil, right: nil,
topConstant: 40,
leftConstant: 30,
bottomConstant: 0,
rightConstant: 0,
widthConstant: 0,
heightConstant: 0)
firstDropDownlabel.widthAnchor.constraint(equalToConstant: 130).isActive = true
firstDropDownlabel.heightAnchor.constraint(equalToConstant: 65).isActive = true
}

如果要在特定位置添加 UIView,请使用

yourStackView.insertArrangedSubview(yourView, at: index)