2

我有一个关于在以下场景中使用布局约束的问题。一切都在代码中(没有笔尖或故事板)。我想创建一个包含任意数量的项目行的视图。每行包含任意数量的子视图。

为了创建这个视图,我想传入一个深度为两层的数组。第一级数组包含每一行。第二级数组包含每一行中的元素。因此,例如,这可能如下所示:

NSArray *elements = @[@[subview1, subview2, subview3], @[subview4], @[subview5, subview6]]

在这个数组中,将有 3 行:

1) 第 1 行:subview1、subview2、subview3
2) 第 2 行:subview4
3) 第 3 行:subview5、subview6

我希望将这些元素格式化为:

- 行都应该是它们父视图的全宽(为此我们可以假设它是屏幕的大小)
- 一行中的每个元素应该是相同的宽度并且它们之间具有相同的空间量(比如如果有 4 个元素,则 1&2 和 2&3 之间的空间可能是 10pt)
- 每行之间应该有相同数量的垂直空间(例如,每行之间的垂直空间为 10pt)

在上面的场景中,第 1 行将有 3 个等宽等间距的子视图,第 2 行将有 1 个占据行全宽度的子视图,第 3 行将有 2 个等宽且等间距的子视图分开。

所以,问题是,我该怎么做?!

我已经为此工作了一段时间,但我的理解似乎并没有变得更好。帮助将不胜感激!

4

2 回答 2

9

我认为这样的事情应该有效。我试着做一般,所以它应该适用于不同数量的行和每行的元素。

#define subviewHeight 44
#define spaceFromTop 10
#define spaceFromSide 10
#define subviewVerticalSpacing 10
#define subviewHorizontalSpacing 10

@implementation ViewController

- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];

    UIButton *subview1 = [UIButton buttonWithType:UIButtonTypeRoundedRect];
    [subview1 setTitle:@"View 1" forState:UIControlStateNormal];
    UIButton *subview2 = [UIButton buttonWithType:UIButtonTypeRoundedRect];
    [subview2 setTitle:@"View 2" forState:UIControlStateNormal];
    UIButton *subview3 = [UIButton buttonWithType:UIButtonTypeRoundedRect];
    [subview3 setTitle:@"View 3" forState:UIControlStateNormal];
    UIButton *subview4 = [UIButton buttonWithType:UIButtonTypeRoundedRect];
    [subview4 setTitle:@"View 4" forState:UIControlStateNormal];
    UIButton *subview5 = [UIButton buttonWithType:UIButtonTypeRoundedRect];
    [subview5 setTitle:@"View 5" forState:UIControlStateNormal];
    UIButton *subview6 = [UIButton buttonWithType:UIButtonTypeRoundedRect];
    [subview6 setTitle:@"View 6" forState:UIControlStateNormal];

    NSArray *arrayOfSubviews = @[@[subview1, subview2, subview3], @[subview4], @[subview5, subview6]];

    [self addSubviewsWithConstraints:arrayOfSubviews];

}

-(void)addSubviewsWithConstraints:(NSArray *) arrayOfArrays {
    NSMutableArray *arrayOfViewsDicts = [NSMutableArray array]; // make the views dictionaries needed for the views parameter of constraintsWithVisualFormat:options:metrics:views:. One for each row.

    for (NSArray *array in arrayOfArrays) {
        NSMutableDictionary *dict = [NSMutableDictionary dictionary];
        for (int i = 0; i < array.count; i++) {
            [dict setObject:array[i] forKey:[NSString stringWithFormat:@"view%d",i]];
        }
        [arrayOfViewsDicts addObject:dict];
    }

    for (int i=0; i<arrayOfArrays.count; i++) {
        NSString *formatString = [self makeConstraintsForRowOfSubviews:arrayOfArrays[i] withViewsDict:arrayOfViewsDicts[i]]; // Make the format string for a row of subviews

        for (id subview in arrayOfArrays[i]) {
            [subview setTranslatesAutoresizingMaskIntoConstraints:NO];
            [self.view addSubview:subview];
            NSLayoutConstraint *heightCon = [NSLayoutConstraint constraintWithItem:subview attribute:NSLayoutAttributeHeight relatedBy:0 toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1 constant:subviewHeight];
            [subview addConstraint:heightCon];

            NSLayoutConstraint *verticalCon = [NSLayoutConstraint constraintWithItem:subview attribute:NSLayoutAttributeTop relatedBy:0 toItem:self.view attribute:NSLayoutAttributeTop multiplier:1 constant:spaceFromTop + ((subviewHeight + subviewVerticalSpacing)*i)];
            [self.view addConstraint:verticalCon];
        }
        NSArray *cons = [NSLayoutConstraint constraintsWithVisualFormat:formatString options:NSLayoutFormatAlignAllBottom metrics:nil views:arrayOfViewsDicts[i]];

        [self.view addConstraints:cons];
    }
}


-(NSString *) makeConstraintsForRowOfSubviews:(NSArray *) arrayOfSubviews withViewsDict:(NSDictionary *) viewsDict {
    NSMutableString *formatString = [NSMutableString string];

    for (int i = 0; i<arrayOfSubviews.count; i++) {
        if (i == 0) {
            [formatString appendFormat:@"|-%d-[%@]",spaceFromSide,[viewsDict allKeysForObject:arrayOfSubviews[i]][0]];
        }else{
            [formatString appendFormat:@"-%d-[%@(==view0)]",subviewHorizontalSpacing,[viewsDict allKeysForObject:arrayOfSubviews[i]][0]];
        }
    }

    [formatString appendFormat:@"-%d-|",spaceFromSide];
    return formatString;
}

这段代码产生了这个结果:

在此处输入图像描述

于 2013-04-25T06:37:20.353 回答
0

可以使用自动布局来做到这一点,但我认为如果您只是将 aUICollectionView与您自己的自定义UICollectionViewLayout子类一起使用,您的代码实际上会更容易理解。

如果您真的想使用自动布局来做到这一点,我认为您应该通过引入中间级别的“行”视图来使您的视图层次结构与您的数组层次结构相匹配:

gridView
├─ rowView0
│  ├─ subview1
│  ├─ subview2
│  └─ subview3
├─ rowView1
│  └─ subview4
└─ rowView2
   ├─ subview5
   └─ subview6

现在您可以像这样对行设置约束:

// These constraints force the rows to collectively fill gridView:
rowView0.top = gridView.top
rowView1.top = rowView0.bottom + 10
rowView2.top = rowView1.bottom + 10
gridView.bottom = rowView2.bottom

// These constraints force the rows to have equal heights:
rowView1.height = rowView0.height
rowView2.height = rowView0.height

// These constraints force the rows to each be the width of gridView:
rowView0.leading = gridView.leading
rowView0.trailing = gridView.trailing
rowView1.leading = gridView.leading
rowView1.trailing = gridView.trailing
rowView2.leading = gridView.leading
rowView2.trailing = gridView.trailing

这些约束应该是必要的(即,不会过度约束)和充分的(即,不会模棱两可)。

然后,您可以对每一行应用相同的约束方案,交换 X 轴和 Y 轴。例如,您将在第 0 行中使用这些约束:

// These constraints collectively force the cells to fill rowView0:
subview1.leading = rowView0.leading
subview2.leading = subview1.trailing + 10
subview3.leading = subview2.trailing + 10
rowView0.trailing = subview3.trailing

// These constraints force the cells to have equal widths:
subview2.width = subview1.width
subview3.width = subview1.width

// These constraints force the cells to each be the height of rowView0:
subview1.height = rowView0.height
subview2.height = rowView0.height
subview3.height = rowView0.height
于 2013-04-25T05:01:05.347 回答