我正构建的部分屏幕包含一个n
个视图的部分。我在代码中动态生成这些视图 - 它们是简单的UIView
子类。
我在此屏幕上使用AutoLayout约束,我希望每个视图自动将其自身定位在其上方视图下方15px左右。
在Xcode中,可以创建spacing to nearest neighbor constraint,这似乎完全符合我的要求。
但是,我似乎无法找到任何显示如何在代码中创建它的示例。
是否可以通过编程方式创建“与最近邻居间距”约束?
答案 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属性,以便在将来(已创建视图的)大小发生任何变化时不会出现任何约束冲突。