UIStackView的简单使用与理解

2018/10/10 posted in  iOS

之前一直在吐槽iOS的布局方式(frame和autolayout)相比前端的flex布局方式很落后,也在想有没有其它的方式来改善。最近偶然发现UIStackView的存在(苹果爸爸原谅我😂),了解后发现其中的使用与布局方式类似于flex布局,感觉这就是苹果爸爸借鉴flex布局特点所构造的一种布局实现方式吧。

实现方式

这里我们看一下如何简单的使用stackview来创造一个拥有众多子item的水平视图。代码如下:

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    
    containerView = [[UIStackView alloc]initWithFrame:CGRectMake(0, 100, CGRectGetWidth(self.view.bounds), 200)];
    //子视图布局方向:水平或垂直
    containerView.axis = UILayoutConstraintAxisHorizontal;//水平布局
    //子控件依据何种规矩布局
    containerView.distribution = UIStackViewDistributionFillEqually;//子控件均分
    //子控件之间的最小间距
    containerView.spacing = 10;
    //子控件的对齐方式
    containerView.alignment = UIStackViewAlignmentFill;
    NSArray *tempArray = @[@"1",@"2",@"3",@"4"];
    for (NSInteger i = 0; i < 4; i++) {
//        UIView *view = [[UIView alloc]init];
        UILabel *label = [[UILabel alloc] init];
        label.textAlignment = NSTextAlignmentCenter;
        label.backgroundColor = [UIColor colorWithRed:random()%256/255.0 green:random()%256/255.0 blue:random()%256/255.0 alpha:1];
        label.numberOfLines = 0;
        label.text = tempArray[i];
        
        [containerView addArrangedSubview:label];
        
    }
    [self.view addSubview:containerView];
}

可以看到stackView的使用和view没有大的区别,使用时根绝需要来设置stackView的axis(布局方向),distribution(子控件依据何种规矩布局),spacing(子控件之间的最小间距),alignment(子控件的对齐方式)等属性。

这里详细说明一下个属性的主要参数:

axis:
子控件的布局方向,水平(UILayoutConstraintAxisHorizontal)或垂直(UILayoutConstraintAxisVertical), 这个不用过多解释了

UIStackViewDistribution:

UIStackViewDistributionFill :它就是将 arrangedSubviews 填充满整个 StackView ,如果设置了spacing,那么这些 arrangedSubviews 之间的间距就是spacing。如果减去所有的spacing,所有的 arrangedSubview 的固有尺寸( intrinsicContentSize )不能填满或者超出 StackView 的尺寸,那就会按照 Hugging 或者 CompressionResistance 的优先级来拉伸或压缩一些 arrangedSubview 。如果出现优先级相同的情况,就按排列顺序来拉伸或压缩。

UIStackViewDistributionFillEqually :这种就是 StackView 的尺寸减去所有的spacing之后均分给 arrangedSubviews ,每个 arrangedSubview 的尺寸是相同的。

UIStackViewDistributionFillProportionally :这种跟FillEqually差不多,只不过这个不是讲尺寸均分给 arrangedSubviews ,而是根据 arrangedSubviews 的 intrinsicContentSize 按比例分配。

UIStackViewDistributionEqualSpacing :这种是使 arrangedSubview 之间的spacing相等,但是这个spacing是有可能大于 StackView 所设置的spacing,但是绝对不会小于。这个类型的布局可以这样理解,先按所有的 arrangedSubview 的 intrinsicContentSize 布局,然后余下的空间均分为spacing,如果大约 StackView 设置的spacing那这样就OK了,如果小于就按照 StackView 设置的spacing,然后按照 CompressionResistance 的优先级来压缩一个 arrangedSubview 。

UIStackViewDistributionEqualCentering :这种是使 arrangedSubview 的中心点之间的距离相等,这样没两个 arrangedSubview 之间的spacing就有可能不是相等的,但是这个spacing仍然是大于等于 StackView 设置的spacing的,不会是小于。这个类型布局仍然是如果 StackView 有多余的空间会均分给 arrangedSubviews 之间的spacing,如果空间不够那就按照 CompressionResistance 的优先级压缩 arrangedSubview 。

alignment:

UIStackViewAlignmentFill = 默认方式, 如果子控件水平布局, 则指子控件的垂直方向填充满stackView. 反之亦然

UIStackViewAlignmentLeading = 如果子控件竖直布局, 则指子控件左边对齐stackView左边. 反之亦然, 即 UIStackViewAlignmentTop = UIStackViewAlignmentLeading。

UIStackViewAlignmentTop = UIStackViewAlignmentLeading,

UIStackViewAlignmentFirstBaseline = 根据上方基线布局所有子视图的 y 值(适用于 Horizontal 模式)

UIStackViewAlignmentLastBaseline = 根据下方基线布局所有子视图的 y 值(适用于 Horizontal 模式)

UIStackViewAlignmentCenter = 中心对齐

UIStackViewAlignmentTrailing = 如果子控件竖直布局, 则指子控件左边对齐stackView右边. 反之亦然, 即UIStackViewAlignmentBottom = UIStackViewAlignmentTrailing

UIStackViewAlignmentBottom = UIStackViewAlignmentTrailing

这里还要说明几个方法:addArrangedSubviewremoveArrangedSubviewinsertArrangedSubview,日常view的添加和子视图从复视图删除使用的是addSubviewremoveFromSuperview

其中完整方法如下:

初始化数组:
- (instancetype)initWithArrangedSubviews:(NSArray *)views;
添加子视图: 
- (void)addArrangedSubview:(UIView *)view;
移除子视图:
- (void)removeArrangedSubview:(UIView *)view;
根据下标插入视图:
- (void)insertArrangedSubview:(UIView *)viewatIndex:(NSUInteger)stackIndex;

注意: addArrangedSubview 和 insertArrangedSubview, 会把子控件加到arrangedSubviews数组的同时添加到StackView的subView数组中,但是removeArrangedSubview, 只会把子控件从arrangedSubviews数组中移除,不会从subviews中移除,如果需要调用removeFromSuperview

若我们需要删除stackView中subView数组的最后一个视图,可以用如下方式:

//removeArrangedSubview, 只会把子控件从arrangedSubviews数组中移除,
//不会从subviews中移除,如果需要可调用removeFromSuperview
UIView *view = [_containerView.subviews lastObject];
[_containerView removeArrangedSubview:view];
[view removeFromSuperview];

到此stackView的一个简单使用方式就知道了。