使用autolayout分配中心

时间:2014-11-30 01:42:40

标签: ios macos uiview autolayout nsview

将问题简化为X轴以简化。

给定具有共同祖先的2个或更多视图的数组,我们如何使用autolayout均匀分布其X中心,以便:

  • 第一个视图与共同的祖先保持一致。
  • 最后一个视图与共同的祖先对齐。
  • 视图的X中心均匀分布。

数组按顺序包含视图。对于这些观点及其共同祖先,没有其他任何东西可以被假定。特别是:

  • 视图可以有不同的大小。
  • 共同祖先的起源可能与(0,0)不同。
  • 共同祖先的宽度或任何视图可能随时更改。

在代码中(使用UIView,但同样适用于NSView):

- (NSArray*)constraintsByDistributingCenterXOfViews:(NSArray*)views
{
    const NSInteger count = views.count;
    NSAssert(count >= 2, @"Array must contain at least 2 views");

    NSMutableArray *constraints = [NSMutableArray array];

    UIView *sharedAncestor = [views sharedAncestor]; // Util method

    UIView *firstView = [views firstObject];
    [constraints addObject:[firstView constraintByAligningLeftToView:sharedAncestor]]; // Util method

    UIView *previousView = [self firstObject];
    for (NSInteger i = 1; i < count - 1; i++)
    {
        UIView *currentView = views[i];
        NSLayoutConstraint *constraint = ? // What would go here?
        [constraints addObject:constraint];
        previousView = currentView;
    }

    UIView *lastView = [views lastObject];
    [constraints addObject:[lastView constraintByAligningRightToView:sharedAncestor]]; // Util method

    return constraints;
}

2 个答案:

答案 0 :(得分:1)

我认为这应该有效。它创建了间隔物,并在两侧的视图中心和间隔物的左右边缘之间增加了约束。假设您已创建了所有视图,但尚未将其添加到超级视图中,则会设置该方法。像这样称呼它,

[self equallySpaceCentersOfViews:@[l1,l2,l3,l4] inView:someView];
// add a constraint here to vertically place one of the views in someView (all the views have their centerY's equal)

这是方法,

-(void)equallySpaceCentersOfViews:(NSArray *) views inView:(UIView *) superview {

    //create and add all the spacers to the superview
    NSMutableArray *spacers = [NSMutableArray new];
    for (int i =0; i<views.count - 1; i++) {
        UIView *spacer = [UIView new];
        spacer.translatesAutoresizingMaskIntoConstraints = NO;
        [superview addSubview:spacer];
        [spacers addObject:spacer];
    }

    //Add all the views to the superview.
    for (UIView *obj in views) {
        [obj setTranslatesAutoresizingMaskIntoConstraints:NO];
        [superview addSubview:obj];
    }

    // Create the constraints to the two edges
    [superview addConstraint:[NSLayoutConstraint constraintWithItem:views[0] attribute:NSLayoutAttributeLeft relatedBy:0 toItem:superview attribute:NSLayoutAttributeLeft multiplier:1 constant:0]];
    [superview addConstraint:[NSLayoutConstraint constraintWithItem:views.lastObject attribute:NSLayoutAttributeRight relatedBy:0 toItem:superview attribute:NSLayoutAttributeRight multiplier:1 constant:0]];

    //Create the constraints between the views and spacers
    [views enumerateObjectsUsingBlock:^(UIView *obj, NSUInteger idx, BOOL *stop) {
        [superview addConstraint:[NSLayoutConstraint constraintWithItem:obj attribute:NSLayoutAttributeCenterX relatedBy:0 toItem:spacers[idx] attribute:NSLayoutAttributeLeft multiplier:1 constant:0]];
        [superview addConstraint:[NSLayoutConstraint constraintWithItem:obj attribute:NSLayoutAttributeCenterY relatedBy:0 toItem:spacers[idx] attribute:NSLayoutAttributeCenterY multiplier:1 constant:0]];
        [superview addConstraint:[NSLayoutConstraint constraintWithItem:views[idx+1] attribute:NSLayoutAttributeCenterX relatedBy:0 toItem:spacers[idx] attribute:NSLayoutAttributeRight multiplier:1 constant:0]];
        [superview addConstraint:[NSLayoutConstraint constraintWithItem:views[idx+1] attribute:NSLayoutAttributeCenterY relatedBy:0 toItem:spacers[idx] attribute:NSLayoutAttributeCenterY multiplier:1 constant:0]];
        if (idx+1 == views.count-1) *stop = YES;
    }];

    //Make all the spacers have the same width
    [spacers enumerateObjectsUsingBlock:^(UIView *spacer, NSUInteger idx, BOOL *stop) {
        [superview addConstraint:[NSLayoutConstraint constraintWithItem:spacer attribute:NSLayoutAttributeWidth relatedBy:0 toItem:spacers[idx+1] attribute:NSLayoutAttributeWidth multiplier:1 constant:0]];
        if (idx+1 == spacers.count-1) *stop = YES;
    }];
}

这给出了以下四个不同宽度标签的结果,

enter image description here

答案 1 :(得分:0)

我能想到实现这一目标的最佳方法是使用方法......

constraintWithItem: attribute: relatedBy: toItem: attribute: multiplier: constant:

要在超视图中均匀地分隔子视图中心,将子视图的NSLayoutAttributeCenterX约束到NSLayoutAttributeCenterX超级视图,并使用乘数变量来区分它们......

CGFloat multiplier = (2.0 / (count - 1)) * position

其中countarray.countpositionarray内的观看索引。这会使视图均匀分隔,但第一个和最后一个视图不会按要求对齐...

UIViews spaced but not aligned!

为了正确对齐第一个视图,我们只需将constant设置为frame.size.wdith / 2

为了正确对齐上一个视图,我们只需将constant设置为-(frame.size.width / 2) ...

First and Last fixed

越来越近,但间距有点不合适,所以我们需要再次使用constant和偏移。首先,我们需要第一个和最后一个视图的总宽度......

UIView *viewFirst = array[0];
CGFloat widthFirst = viewFirst.frame.size.width;
UIView *viewLast = array[array.count - 1];
CGFloat widthLast = viewLast.frame.size.width;
CGFloat offset = widthFirst + widthLast;

现在我们可以使用此功能,结合countposition来抵消所有其他视图......

CGFloat middle = (count - 1) / 2; // work out the centre of the array
CGFloat constant = ((offset / 2) / (count - 1)) * -(position - middle);

将这一切放在一个方法中......

- (void)addConstraintsToView:(UIView *)view position:(NSInteger)position count:(NSInteger)count offset:(CGFloat)offset {

    view.translatesAutoresizingMaskIntoConstraints = NO;

    [view addConstraint:[NSLayoutConstraint constraintWithItem:view
                                                     attribute:NSLayoutAttributeWidth
                                                     relatedBy:NSLayoutRelationEqual
                                                        toItem:nil
                                                     attribute:NSLayoutAttributeNotAnAttribute
                                                    multiplier:1.0
                                                      constant:view.frame.size.width]];

    [view addConstraint:[NSLayoutConstraint constraintWithItem:view
                                                     attribute:NSLayoutAttributeHeight
                                                     relatedBy:NSLayoutRelationEqual
                                                        toItem:nil
                                                     attribute:NSLayoutAttributeNotAnAttribute
                                                    multiplier:1.0
                                                      constant:view.frame.size.height]];

    CGFloat multiplier = (2.0 / (count - 1)) * position;
    CGFloat middle = (count - 1) / 2;
    CGFloat standard = ((offset / 2) / (count - 1)) * -(position - middle);
    CGFloat constant = (position == 0) ? view.frame.size.width / 2 : (position == count - 1) ? -view.frame.size.width / 2 : standard;

    [self.mainView addConstraint:[NSLayoutConstraint constraintWithItem:view
                                                              attribute:NSLayoutAttributeCenterX
                                                              relatedBy:NSLayoutRelationEqual
                                                                 toItem:self.mainView
                                                              attribute:NSLayoutAttributeCenterX
                                                             multiplier:multipler
                                                               constant:constant]];

    [self.mainView addConstraint:[NSLayoutConstraint constraintWithItem:view
                                                              attribute:NSLayoutAttributeCenterY
                                                              relatedBy:NSLayoutRelationEqual
                                                                 toItem:self.mainView
                                                              attribute:NSLayoutAttributeCenterY
                                                             multiplier:1.0
                                                               constant:0.0]];

}

所以在viewDidLoad中,计算偏移并迭代遍历每次调用此方法的views数组......

UIView *viewFirst = array[0];
CGFloat widthFirst = viewFirst.frame.size.width;
UIView *viewLast = array[array.count - 1];
CGFloat widthLast = viewLast.frame.size.width;
CGFloat offset = widthFirst + widthLast;

for(NSInteger i = 0; i < array.count; i++) {
    UIView *view = array[i];
    [self.mainView addSubview:view];
    [self addConstraintsToView:view position:i count:array.count offset:offset];
}

嘿presto ...

UIViews horizontally spaced to their centres!

编辑: 只是注意到这不是很正确,因为当第一个和最后一个视图的宽度不同时,它不能正确地偏移,但希望你能得到这个想法(如果它们都是相同的话,它会完美地工作)