以编程方式创建"间隔到最近邻居"约束

时间:2014-02-12 20:08:21

标签: ios iphone autolayout nslayoutconstraint

我正构建的部分屏幕包含一个n个视图的部分。我在代码中动态生成这些视图 - 它们是简单的UIView子类。

我在此屏幕上使用AutoLayout约束,我希望每个视图自动将其自身定位在其上方视图下方15px左右。

在Xcode中,可以创建spacing to nearest neighbor constraint,这似乎完全符合我的要求。

但是,我似乎无法找到任何显示如何在代码中创建它的示例。

是否可以通过编程方式创建“与最近邻居间距”约束?

3 个答案:

答案 0 :(得分:1)

您可以创建约束字典和约束字符串并以编程方式应用它们。

向字典中添加项目和键是微不足道的,所以我不会处理它。请记住,约束系统中的每个视图都必须在字典中。

创建格式字符串是有趣的。大概你想在某个视图下添加你的视图,比如它有NSString键topView。格式字符串的第一部分看起来像

NSString *constraintBase = [NSString stringWithFormat:@"V:topView"];

对于您要添加的每个视图,您都要添加到该字符串

NSString *constraintString = [constraintBase stringByAppendingString:[NSString stringWithFormat:@"-15-%@", viewDictionaryKey]];

最后像往常一样应用constraintString,一个构造的视觉约束格式字符串。

[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:constraintString options:0 metrics:nil views:viewsToConstrain]];

答案 1 :(得分:1)

当我偶然发现你的问题并决定扩展我的小型演示时,我实际上正在做一些自动布局和动画的个人练习,你可以download here

如果我在下面正确理解你,可以使用一段代码作为灵感。只需很少的努力,它就可以通过删除视图来扩展,也可以通过视图的动态高度进行扩展。

请注意我的代码包含带动画和不带动画的解决方案 - 后者当然更简单。

#import "Demo2ViewController.h"

@interface Demo2ViewController ()
{
    NSMutableArray *_viewList;
    NSDictionary *_metrics;
}
@end

@implementation Demo2ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view.

    [[self view] setBackgroundColor:[UIColor colorWithRed:.95 green:.95 blue:.95 alpha:1.0]];

    _metrics = @{@"height": @30,  // height of the views being added
                 @"space":  @15}; // space between two views

    // the first view
    UITextView *textView = [[UITextView alloc] initWithFrame:CGRectNull];
    _viewList = [[NSMutableArray alloc] initWithObjects:textView, nil];
    textView.text = [NSString stringWithFormat:@"view: %lu", (unsigned long)[_viewList count]];

    // a button to add more views
    UIButton *buttonAddView = [[UIButton alloc] initWithFrame:CGRectNull];
    [buttonAddView setTitle:@"add new view" forState:UIControlStateNormal];
    [buttonAddView setTitleColor:[UIColor blueColor] forState:UIControlStateNormal];
    [buttonAddView addTarget:self action:@selector(buttonPushed:) forControlEvents:UIControlEventTouchDown];

    NSDictionary *subviews = NSDictionaryOfVariableBindings(textView, buttonAddView);

    for (id view in [subviews allValues]) {
        [[self view] addSubview:view];
        [view setTranslatesAutoresizingMaskIntoConstraints:NO];
    }

    // initial constraints
    [[self view] addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-[textView]-|" options:0 metrics:nil views:subviews]];
    [[self view] addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-[buttonAddView]-|" options:0 metrics:nil views:subviews]];
    [[self view] addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-[textView(==height)]" options:0 metrics:_metrics views:subviews]];
    [[self view] addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[buttonAddView]-|" options:0 metrics:nil views:subviews]];
}

-(void)buttonPushed:(UIButton*)button
{
    UITextView *prevView = [_viewList lastObject]; // get reference to previous view

    // create a new view
    UITextView *newView = [[UITextView alloc] initWithFrame:CGRectNull];
    [[self view] addSubview:newView];
    [newView setTranslatesAutoresizingMaskIntoConstraints:NO];
    [_viewList addObject:newView];
    newView.text = [NSString stringWithFormat:@"view: %lu", (unsigned long)[_viewList count]];

    NSDictionary *subviews = NSDictionaryOfVariableBindings(prevView, newView);

    [[self view] addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-[newView]-|" options:0 metrics:nil views:subviews]];

#if 0
    // without animation
    [[self view] addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[prevView]-space-[newView(==height)]" options:0 metrics:_metrics views:subviews]];
    [[self view] layoutIfNeeded];
#else
    // with animation

    // to begin with the new view gets zero height and no space to previous view
    NSArray *tempConstraints = [NSLayoutConstraint constraintsWithVisualFormat:@"V:[prevView][newView(==0)]" options:0 metrics:nil views:subviews];

    [[self view] addConstraints:tempConstraints];
    [[self view] layoutIfNeeded]; // to ensure zero height is the starting point for the animation

    [newView setAlpha:0.0f]; // starting point for fade-in

    [UIView animateWithDuration:0.25f animations:^{
        [[self view] removeConstraints:tempConstraints]; // remove zero height constraint
        [[self view] addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[prevView]-space-[newView(==height)]" options:0 metrics:_metrics views:subviews]]; // add final constraints
        [newView setAlpha:1.0f]; // fade-in
        [[self view] layoutIfNeeded];
    }];
#endif
}

@end

答案 2 :(得分:0)

这里以编程方式生成的视图被添加到父视图中。 创建视图时,应将其约束添加到其中。

但是约束必须与前一个视图一起映射,因此我们需要识别上一个视图(父视图中的最后一个视图)

NSArray *subViewList = [_vwParentView subviews];
UIView *lastView;
if (subViewList.count > 0) {
    lastView = [subViewList lastObject];
}

以上代码将有助于查找上次创建的视图。 当以编程方式创建视图并将其添加为子视图时,视图将作为parrentView的堆栈添加,因此最后创建的视图将是subViewList数组中的最后一个对象

注意:假设单独的视图被指定为父视图,最初没有子视图。

UIView *contentView = [[UIView alloc]init];

NSLayoutConstraint *widthConstraint = [NSLayoutConstraint constraintWithItem:contentView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeWidth multiplier:1.0 constant:initialWidth];
[contentView addConstraint:widthConstraint];

NSLayoutConstraint *heightConstraint = [NSLayoutConstraint constraintWithItem:contentView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeHeight multiplier:1.0 constant:initialHeight];
[contentView addConstraint:heightConstraint];

contentView.translatesAutoresizingMaskIntoConstraints = NO;

[_vwParentView addSubview:contentView];

NSLayoutConstraint *gapMaintainTopConstraint;
if (lastView == nil) {
    gapMaintainTopConstraint = [NSLayoutConstraint constraintWithItem:contentView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:_vwParentView attribute:NSLayoutAttributeTop multiplier:1.0 constant:15];
}
else
{
    gapMaintainTopConstraint = [NSLayoutConstraint constraintWithItem:contentView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:lastView attribute:NSLayoutAttributeBottom multiplier:1.0 constant:15];
}
[_vwParentView addConstraint:gapMaintainTopConstraint];

如果要在将来更改添加的视图的大小,应该通过更改其widthConstraint或heightConstraint来实现,只有那时与之关联的约束(以保持特定的间隙)才有效。此后不应使用框架更改尺寸。 含义 - 基于约束的视图应仅使用约束来处理。

只有在创建的视图作为子视图添加后,才可以创建“与最近邻居间距”约束。

应禁用已创建视图的translatesAutoresizingMaskIntoConstraints属性,以便在将来(已创建视图的)大小发生任何变化时不会出现任何约束冲突。