以编程方式在 UIStackView 中添加视图
Posted
技术标签:
【中文标题】以编程方式在 UIStackView 中添加视图【英文标题】:Add views in UIStackView programmatically 【发布时间】:2015-08-24 00:52:04 【问题描述】:我正在尝试。 现在我的代码是:
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 个视图,它是黑色的。(view1 也获取 view2 的参数)
【问题讨论】:
你检查过你的插座了吗?您是否在运行时记录了子视图? 使用addArrangedSubview:
,而不是addSubview:
【参考方案1】:
堆栈视图使用固有内容大小,因此使用布局约束来定义视图的尺寸。
有一种快速添加约束的简单方法(示例):
[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 上测试的
【讨论】:
使用堆栈视图的要点是它们对开发人员隐藏了约束管理的细节。正如您所指出的,这依赖于具有内在内容大小的子视图,默认UIViews
没有。如果原始发帖人使用UILabel
实例而不是UIView
,那么他的代码将按预期工作。我在下面添加了一个示例来演示这一点。
当我这样做时“[view1.heightAnchor constraintEqualToConstant:100].active = true;”我在 ios 8 上遇到错误。它仅在 ios 9 上工作。我知道 uistackview 适用于 ios 9+,但我如何为 ios 8 设置 heightAncor 约束
我的 StackView 是使用故事板添加的。在运行时,我将几个 UIView(包含其他子视图)添加到 stackView。加载数据后,stackView 中的所有视图都具有相同的高度,它们不会根据内容调整大小。 @user1046037
您是否为这些单独的视图设置了约束,以便它们的大小会根据内容而有所不同?
这是管理等宽或等高视图的最佳方式【参考方案2】:
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 的回答。
【讨论】:
启动 iOS 8,可以使用activate(_:)
批量激活约束,通常这样做效率更高developer.apple.com/documentation/uikit/nslayoutconstraint/…
Swift 5 版本:***.com/a/58827722/2273338【参考方案3】:
在 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)
])
【讨论】:
【参考方案4】:UIStackView
在内部使用约束来定位其排列的子视图。究竟创建什么约束取决于堆栈视图本身的配置方式。默认情况下,堆栈视图将创建约束,将其排列的子视图布置在水平线上,将前导视图和尾随视图固定到它自己的前导和后沿。因此,您的代码将生成如下所示的布局:
|[view1][view2]|
分配给每个子视图的空间由许多因素决定,包括子视图的固有内容大小及其抗压缩性和内容拥抱优先级。默认情况下,UIView
实例不定义内在内容大小。这通常由子类提供,例如UILabel
或UIButton
。
由于两个新的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
的主要好处——它向开发人员隐藏了约束管理的(通常是丑陋的)细节。
【讨论】:
我有同样的问题,这适用于标签,但不适用于文本字段(或视图,就此而言)。此外,我发现的所有教程都使用标签、图像或按钮。这是否意味着对于与这些 UI 元素不同的任何东西,我们都不能使用这种方法? @physicalattraction 添加到堆栈视图的视图必须具有固有的内容大小。我记得,文本字段的固有大小基于字段的内容(这很奇怪)。UIView
的实例本身没有固有大小。您可能需要对这些视图应用额外的约束,以便堆栈视图知道使它们有多大。
有道理,所以我正在尝试。现在我在带有文本字段的 xib 中构建了一个视图,我给出了一个 30 像素的显式高度约束。我还做了以下两个约束:MyTextField.top = top+20
和bottom = MyTextField.bottom+20
。我希望这会给我的视图带来 70 的内在高度,但它却抱怨约束冲突。你知道这里发生了什么吗?我想将这个视图放在我的 UIStackView 中。
我想我知道问题出在哪里。堆栈视图会自动将其最终排列的子视图固定到其底部边缘。因此堆栈视图试图使您的容器视图与自身大小相同。但是,您已经告诉这个视图它必须是 70 像素高,这会产生冲突。尝试在容器视图之后立即创建一个额外的空视图,并将其垂直内容拥抱优先级设置为 0。将容器视图的垂直内容拥抱优先级设置为 1000。这将导致空视图拉伸以填充堆栈中的空白空间查看。
顺便说一下,我假设您正在使用垂直堆栈视图。您可能还想查看我写的关于堆栈视图的这篇文章:dzone.com/articles/…【参考方案5】:
您必须设置您的分发类型。 在您的代码中,只需添加:
self.stack1.distribution = UIStackViewDistributionFillEqually;
或者您可以直接在界面构建器中设置分发。 例如:
希望有所帮助;) 拉皮努。
【讨论】:
【参考方案6】:以下两行解决了我的问题
view.heightAnchor.constraintEqualToConstant(50).active = true;
view.widthAnchor.constraintEqualToConstant(350).active = true;
Swift 版本 -
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
【讨论】:
【参考方案7】:对于当您尝试在堆栈视图中隐藏任何视图时接受的答案,约束不正确。
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)>"
)
原因是在view
中隐藏stackView
时,它会将高度设置为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
【讨论】:
【参考方案8】: //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 的答案
【讨论】:
【参考方案9】:在我的情况下,我期望的事情是我错过了这一行:
stackView.translatesAutoresizingMaskIntoConstraints = false;
之后,无需对我排列的子视图设置任何约束,stackview 会处理这些。
【讨论】:
【参考方案10】:Swift 5 版本的Oleg Popov's answer,基于user1046037's answer
//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
【讨论】:
【参考方案11】:我刚刚遇到了非常相似的问题。就像之前提到的堆栈视图的尺寸取决于排列的子视图的一种内在内容大小。这是我在 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
【讨论】:
【参考方案12】:如果您要添加许多UIView
s,您可以使用以下扩展名,在其中传递UIView
s 数组,它将按顺序将它们添加到UIStackView
中
extension UIStackView
func addArrangedSubviews(_ subviews: [UIView])
subviews.forEach self.addArrangedSubview($0)
【讨论】:
【参考方案13】:您可以使用以更简单的方式处理UIView
类的.intrinsicContentSize
的子类,而不是对所有约束进行编码。
此解决方案还稍微改进了 Interface Builder,以支持“intrinsicWidth”和“intrinsicHeight”视图。虽然您可以扩展 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 发疯了
或在 swift + 编码、解码、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)
【讨论】:
【参考方案14】:真的不建议设置高度约束...如果可以,永远,永远,永远不要设置高度!您需要检查 UIStackView 中视图的所有约束,并确保底部、顶部、前导和尾随都有约束。有人对我说:这就像一个推墙的人。如果他不推 4 面,其中一面墙会倒在他身上。
【讨论】:
【参考方案15】:试试下面的代码,
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];
希望它有效。如果您需要更多说明,请告诉我。
【讨论】:
以上是关于以编程方式在 UIStackView 中添加视图的主要内容,如果未能解决你的问题,请参考以下文章
以编程方式将一堆自定义视图从 XIB 添加到 UIStackView