如何使用内部 XIB 为动态 UIStackView 设置对齐方式

Posted

技术标签:

【中文标题】如何使用内部 XIB 为动态 UIStackView 设置对齐方式【英文标题】:How to set alignments right for dynamic UIStackView with inner XIB 【发布时间】:2018-11-02 13:48:13 【问题描述】:

我原来的ViewController 只包含一个这样的滚动视图:

现在我也有了自己的xib文件(CheckBoxView),主要由一个按钮组成,看这个截图:

我动态创建一些UIStackViews 并将它们添加到ScrollView 在这些UIStackViews 中我添加了我的xib 文件的多个实例。

我想要实现的是,StackViews 只是垂直堆叠。在 StackViews 中,我的 xib 文件中的 UIViews 也应该垂直堆叠。

目前它看起来像这样:

所以 xib 视图不在整个视图中。由于我使用的是多操作系统引擎,我无法提供 swift/obj-c 代码。但这是我的 Java 代码:

for (ItemConfiguration config : itemInstance.getConfigurations()) 

            List<DLRadioButton> radioButtons = new ArrayList<DLRadioButton>();

            UIStackView configView = UIStackView.alloc().initWithFrame(new CGRect(new CGPoint(0, barHeight), new CGSize(displayWidth, displayHeight - barHeight)));
            configView.setAxis(UILayoutConstraintAxis.Vertical);
            configView.setDistribution(UIStackViewDistribution.EqualSpacing);
            configView.setAlignment(UIStackViewAlignment.Center);
            configView.setSpacing(30);


            for (ConfigurationOption option : config.getOptions()) 
                UIView checkBox = instantiateFromNib("CheckBoxView");
                for (UIView v : checkBox.subviews()) 
                    if (v instanceof DLRadioButton) 
                        ((DLRadioButton) v).setTitleForState(option.getName(), UIControlState.Normal);
                        //((DLRadioButton) v).setIconSquare(true);
                        radioButtons.add((DLRadioButton) v);
                    
                
                configView.addArrangedSubview(checkBox);
            
            // group radiobuttons
            //groupRadioButtons(radioButtons);

            configView.setTranslatesAutoresizingMaskIntoConstraints(false);
            scrollView().addSubview(configView);

            configView.centerXAnchor().constraintEqualToAnchor(scrollView().centerXAnchor()).setActive(true);
            configView.centerYAnchor().constraintEqualToAnchor(scrollView().centerYAnchor()).setActive(true);
        


private UIView instantiateFromNib(String name) 
    return (UIView) UINib.nibWithNibNameBundle(name, null).instantiateWithOwnerOptions(null, null).firstObject();

我需要如何设置对齐方式等以实现我想要的。它应该是这样的:

【问题讨论】:

你的代码中barHeight的值是多少? @AleksandrMedvedev 这是UIApplication.sharedApplication().statusBarFrame().size().height(); 大约 35 块类似的矿石。它只是顶部的状态栏高度。但是既然你提到它,这个UIStackView 的大小不应该是静态的。它应该随着内容动态增长,因此如果内容过多,它会因为外部UIScrollView而变得可滚动 如果您包含一个演示项目,它将更快地解决 【参考方案1】:

我不知道是否有理由不使用UITableView,我强烈建议您使用它。如果不可能,您可以在下面找到一些应该有所帮助的建议。


如果您使用自动布局,您应该为代码中实例化的所有视图设置约束。约束必须全面,ios 才能知道每个视图的位置和大小。

去除多余的约束

configView.centerXAnchor().constraintEqualToAnchor(scrollView().centerXAnchor()).setActive(true);
configView.centerYAnchor().constraintEqualToAnchor(scrollView().centerYAnchor()).setActive(true);

这两个约束对我来说没有意义。您需要将 stackviews 堆叠在 ScrollView 中,但不要居中。如果我正确理解您的目标,则应将其删除

为 UIStackViews 设置宽度/x 位置约束

configView.setTranslatesAutoresizingMaskIntoConstraints(false);
scrollView().addSubview(configView);

在将堆栈视图添加到 ScrollView 之后,您需要为其设置约束。我会迅速提供我的代码,但它看起来与您的 Java 代码所做的非常相似,所以希望您能够毫无困难地转换它:

NSLayoutConstraint.activate([
  configView.leadingAnchor.constraintEqualToAnchor(scrollView.leadingAnchor),
  configView.trailingAnchor.constraintEqualToAnchor(scrollView.trailingAnchor)
]);

为 UIStackViews 设置高度限制

每当您在其中添加排列视图时,StackViews 都不会改变它们的大小。因此,您需要自己计算所需的 stackview 大小并通过约束明确指定它。它应该足以容纳物品和它们之间的空间。我想所有的项目应该是相同的大小,让它是 32 点,那么高度应该是:

let stackViewHeight = items.count * 32 + stackView.space * (items.count + 1)

并为堆栈视图创建新的高度约束:

configView.heightAnchor.constraint(equalToConstant: stackViewHeight).isActive = true

为 UIStackView 设置 y 位置

这是一个更具挑战性的部分,但最重要的是视图在滚动视图中正常工作。

1) 更改循环以了解 UIStackView 的索引

滚动视图应该始终知道其内容的高度,因此您需要了解哪个堆栈视图是顶部,哪个是底部。为此,您需要将每个循环更改为 for(;;) 循环:

for (int i = 0; i < itemInstance.getConfigurations().length; i++) 
  ItemConfiguration config = itemInstance.getConfigurations()[i]
  ...

我不知道你的数组是哪种类型,所以如果它没有下标功能,只需将其替换为相应的方法即可。

2) 设置堆栈视图的顶部锚点

对于数组中的第一个堆栈视图,顶部锚点应该等于滚动视图顶部锚点,对于其他应该是前一个堆栈视图的底部锚点 + 它们之间的间距(例如,本示例中的 8 个点):

if i == 0 
  configView.topAnchor.constraintEqualToAnchor(scrollView.topAnchor, constant: 8).isActive = true
 else 
  let previousConfigView = itemInstance.getConfigurations()[i - 1]
  configView.topAnchor.constraintEqualToAnchor(previousConfigView.bottomAnchor, constant: 8).isActive = true

3) 为最后一个堆栈视图设置底部锚点

如前所述 - 为了让 Scroll View 了解内容大小,我们需要指定相应的约束:

if i == itemInstance.getConfigurations() - 1 
  configView.bottomAnchor.constraintEqualToAnchor(scrollView.bottomAnchor, constant: 8).isActive = true

注意:请注意,所有约束都应设置在已添加到滚动视图的视图上。

【讨论】:

我需要在UITableView 中使用UITableView,不是吗?这不是问题吗?我可以在每个单元格中添加一个UITableView 并在内层添加我的 XIB 视图吗?感谢你的付出!我现在会调查你的答案:) 只是好奇使用 UITableView 是否会更好 @progNewbie,你不需要使用嵌套的UITableViews。相反,您可以在单个 UITableView 中创建几个部分,在数据源委托中实现相应的方法 - developer.apple.com/documentation/uikit/uitableviewdatasource/… 谢谢,我试试这个。

以上是关于如何使用内部 XIB 为动态 UIStackView 设置对齐方式的主要内容,如果未能解决你的问题,请参考以下文章

动态调整自定义 xib 单元格的大小

如何根据 UITextView 动态调整 xib 的大小

如何通过编写 Objective-C 代码来动态更改 Interface Builder xib 文件?

如何动态地将 xib 文件视图适合故事板视图?

如何动态添加自定义视图xib文件

如何为 Xib 设置单元格动态高度的约束...?