这是我的第一个 iOS 项目,因此我在约束和自动布局方面遇到了困难。
这就是我想要做的。我想将 subview 添加到 UIScrollView。这是我以编程方式做的,因为 subview 的数量不是静态的。当我只是添加单个 subview (在代码或 XIB 中)或通过 XIB 添加多个 subview 时,我可以使自动布局和约束完美地工作。但是我无法让它与代码中动态数量的 subview 一起工作。
subview 的许多实例被创建,但它们都在彼此之上,因为约束不起作用。我希望他们在每个下面排列,这就是为什么我试图让约束起作用。 (现在我只是将 subview 的数量设置为 10,但这只是暂时的)
这是我的一些代码(从按下按钮开始):
-(IBAction) buttonTappedid)sender {
UIScrollView *matchScrollView = (UIScrollView *) [self.view viewWithTag:1];
NSMutableArray *matchViewsArray = [[NSMutableArray alloc] init];
for (int i = 0; i <= 9; i++) {
MatchView *newMatchView = [[NSBundle mainBundle] loadNibNamed"MatchView" owner:self options:nil].firstObject;
[matchViewsArray addObject:newMatchView];
}
for (int i = 0; i < [matchViewsArray count]; i++) {
MatchView *newMatchView = matchViewsArray[i];
//I kept some IBOutlets out of the code example here. They work fine with a single subview. or even multiple subviews as long as you don't mind they are all on top of each other due to constraints not working xD
[newMatchView setTranslatesAutoresizingMaskIntoConstraints:NO];
[matchScrollView addSubview:matchViewsArray[i]];
[matchScrollView addConstraint:[NSLayoutConstraint constraintWithItem:matchViewsArray[i]
attribute:NSLayoutAttributeLeading
relatedBy:NSLayoutRelationEqual
toItem:matchScrollView
attribute:NSLayoutAttributeLeading
multiplier:1.0
constant:8.0]];
[matchScrollView addConstraint:[NSLayoutConstraint constraintWithItem:matchViewsArray[i]
attribute:NSLayoutAttributeTrailing
relatedBy:NSLayoutRelationEqual
toItem:matchScrollView
attribute:NSLayoutAttributeTrailing
multiplier:1.0
constant:8.0]];
//If this is not the first object, then the top of the object contraints with the bottom of the previous object
if (i > 0) {
// I think the fault may lie here. Do i need to remove the previous constraint, or will it be overwritten? This did not work however, did i remove it wrong?
//Making sure it only tries to call removeconstraint: once and once only, as the constraint is only put on the else statement which only executes on the very first object. Attempt to fix the code
if (i == 1) {
[matchScrollView removeConstraint:[NSLayoutConstraint constraintWithItem:matchViewsArray[i - 1]
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:matchScrollView
attribute:NSLayoutAttributeTop
multiplier:1.0
constant:8.0]];
}
[matchViewsArray[i - 1] addConstraint:[NSLayoutConstraint constraintWithItem:matchViewsArray[i]
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:matchViewsArray[i - 1]
attribute:NSLayoutAttributeBottom
multiplier:1.0
constant:8.0]];
}
//The top of the first object constraints with the top of the matchScrollView
else {
[matchScrollView addConstraint:[NSLayoutConstraint constraintWithItem:matchViewsArray[i]
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:matchScrollView
attribute:NSLayoutAttributeTop
multiplier:1.0
constant:8.0]];
}
//If this is the last object, then the bottom of the object constraints with the bottom of matchScrollView
if ( i == [matchViewsArray count] - 1) {
//tried removing the constraint created by else statement first before adding a new constraint
[matchViewsArray[i] removeConstraint:[NSLayoutConstraint constraintWithItem:matchViewsArray[i - 1]
attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationEqual
toItem:matchViewsArray[i]
attribute:NSLayoutAttributeTop
multiplier:1.0
constant:8.0]];
[matchScrollView addConstraint:[NSLayoutConstraint constraintWithItem:matchViewsArray[i]
attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationGreaterThanOrEqual
toItem:matchScrollView
attribute:NSLayoutAttributeBottom
multiplier:1.0
constant:8.0]];
}
//else the bottom of the object contraints with the top of the next object
else {
[matchViewsArray[i + 1] addConstraint:[NSLayoutConstraint constraintWithItem:matchViewsArray[i]
attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationEqual
toItem:matchViewsArray[i + 1]
attribute:NSLayoutAttributeTop
multiplier:1.0
constant:8.0]];
}
[matchScrollView addConstraint:[NSLayoutConstraint constraintWithItem:matchViewsArray[i]
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:matchScrollView
attribute:NSLayoutAttributeCenterX
multiplier:1.0
constant:0.0]];
}
}
我不断收到“以 NSException 类型的未捕获异常终止”。我尝试在添加新约束之前删除旧约束,但这没有帮助。除非我删除它们是错误的?
如果我删除所有 if 和 else 语句,只创建一个 subview ,那么约束就可以完美地工作。如果我创建多个 subview ,则应用程序不会崩溃,但 subview 都具有相同的约束并且彼此重叠。因此,通过添加 if 和 else 语句并使用 matchViewsArray[i - 1] 我尝试为多个 subview 设置约束。
此外,即使这段代码有效,真的没有更简单的方法来代替循环中的所有这些 if 和 else 语句吗?
如前所述,这是我的第一个 iOS 项目,因此可能有更有效和更好的方法来实现我想要的行为。
任何帮助将不胜感激。
Best Answer-推荐答案 strong>
很遗憾,我没有制作 iOS 应用程序的许可,也无法按原样对其进行测试,但这个 OSX 代码段可能会有所帮助:
- (void)applicationDidFinishLaunchingNSNotification *)notification
{
NSView *contentView = [window contentView];
NSMutableArray *buttons = [[NSMutableArray alloc] init];
for (int i = 0; i < 10; i++) {
NSButton *button = [[NSButton alloc] initWithFrame:NSZeroRect];
[button setTranslatesAutoresizingMaskIntoConstraints:NO];
[button setTitle:[NSString stringWithFormat"Button %d", (int)pow(10, i)]];
[button setBezelStyle:NSRoundedBezelStyle];
[button setFont:[NSFont systemFontOfSize:[NSFont systemFontSize]]];
[button sizeToFit];
[buttons addObject:button];
[contentView addSubview:button];
}
int count = (int)[buttons count];
// 1. one below other
// 2. all equal width
for (int i = 1; i < count; i++) {
NSView *view1 = buttons[i-1];
NSView *view2 = buttons[i];
NSDictionary *views = NSDictionaryOfVariableBindings(view1, view2);
[contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat"V:[view1]-[view2]"
options:0
metrics:nil
views:views]];
[contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat"H:[view1(==view2)]"
options:0
metrics:nil
views:views]];
}
// 3. first -> top
if (count > 0) {
NSView *view = [buttons firstObject];
NSDictionary *views = NSDictionaryOfVariableBindings(view);
[contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat"V:|-[view]"
options:0
metrics:nil
views:views]];
}
// 4. last -> bottom
if (count > 0) {
NSView *view = [buttons lastObject];
NSDictionary *views = NSDictionaryOfVariableBindings(view);
[contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat"V:[view]-|"
options:0
metrics:nil
views:views]];
}
// 5. left <- all -> right
for (int i = 0; i < count; i++) {
NSView *view = buttons[i];
NSDictionary *views = NSDictionaryOfVariableBindings(view);
[contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat"H:|-[view]-(>=20)-|"
options:0
metrics:nil
views:views]];
}
}
请注意,如果不使用“视觉格式语言”,则无法指定默认的 8/20 间距,因为 constraintsWithVisualFormat:... 会产生特殊的约束(当然,内部带有特殊的私有(private)标志)会自动- 在特定情况下将 8 调整为 6/4/根据 Apple 的 HIG 调整大小。 (您可以尝试在 IB 中放置控件,然后发现默认值并不总是 8,但在运行时突然变为 8!)。
编辑:此外,您可以始终使用简单的#define 或子例程来隐藏 [[[content-constraint-with-contraint-visual-constraint-with-constraints-ponies]]]梯子。
edit2:重构阶段
关于ios - 如何使约束(自动布局)以编程方式工作,我们在Stack Overflow上找到一个类似的问题:
https://stackoverflow.com/questions/29661991/
|