我在IB中有一个UIView,其中有另一个UIView,我用它作为容器视图。在代码中,我创建了三个不同的视图,然后根据应用程序的状态将相应的视图设置为容器视图中的动画。在任何给定时间,三种不同视图中只有一种是有效的。问题是,当我在模拟器中运行不同的iPhone时,我的新子视图不会缩放以匹配容器视图。我正在使用autolayout。出于测试目的,我将我的子视图设置为一个大按钮,它的所有边缘都限制在超视图中。并且容器视图的边缘也受限于它的超视图。我想要的是子视图匹配容器的视图。即按钮拉伸容器视图的整个大小。当在不同的iPhone上运行时,容器视图的大小以及子视图应该相对于不同的iPhone屏幕大小按比例缩放。
下面是我用来初始化子视图并设置相对于容器视图的约束的代码。
UIView *containerView = self.subView;
UIView *newSubview = [[mySubview alloc] init];
[newSubview setTranslatesAutoresizingMaskIntoConstraints:NO];
[self.containerView addSubview:newSubview];
[self.containerView addConstraint:[NSLayoutConstraint constraintWithItem:newSubview attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.containerView attribute:NSLayoutAttributeTop multiplier:1.0 constant:0]];
[self.containerView addConstraint:[NSLayoutConstraint constraintWithItem:newSubview attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:self.containerView attribute:NSLayoutAttributeLeading multiplier:1.0 constant:0]];
[self.containerView addConstraint:[NSLayoutConstraint constraintWithItem:newSubview attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:self.containerView attribute:NSLayoutAttributeWidth multiplier:1.0 constant:0]];
[self.containerView addConstraint:[NSLayoutConstraint constraintWithItem:newSubview attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:self.containerView attribute:NSLayoutAttributeHeight multiplier:1.0 constant:0]];
我似乎无法让这个工作。我对autolayout很新,我不确定我做错了什么,并且想停止撞到这堵墙。任何帮助都会很棒。 :)
*************附加信息**************
对不起,我没有尽可能清楚地说出我的问题。所以这里有更多关于屏幕截图的信息。首先,我将描述我在代码方面做了什么。
在AppDelegate.m中的didFinishLaunchingWithOptions中,我像这样创建MyViewController,
self.myViewController = [[MyViewController alloc] initWithNibName:@"MyViewController" bundle:nil];
在MyViewController.m的viewDidLoad中,我创建mySubview并将其添加到我的containerView并为此创建约束,
UIView *containerView = self.containerView;
UIView *mySubview = [[MySubview alloc] init];
[mySubview setTranslatesAutoresizingMaskIntoConstraints:NO];
[containerView addSubview:mySubview];
NSDictionary *views = NSDictionaryOfVariableBindings(mySubview);
[containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[mySubview]|"
options:0
metrics:nil
views:views]];
[containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[mySubview]|"
options:0
metrics:nil
views:views]];
最后在MySubview.h中的init中,我将nib添加为像这样的子视图,
- (id)init
{
if(self = [super init])
{
NSArray *nibArray = [[NSBundle mainBundle]loadNibNamed:@"MySubview" owner:self options:nil];
[self addSubview:[nibArray objectAtIndex:0]];
}
return self;
}
要注意的一些事情可能有所帮助,
在MyViewController.xib中,我有一个UIView,我将其用作containerView。它有一个IBOutlet到UIView * containerView,是上面引用的那个。我对IB中的containerView的唯一限制是,Superview的前导,尾随和底部空间以及Superview = 44的顶部空间。
对于MySubview.xib,高度和宽度为300,(高度或宽度没有约束)。我觉得这些大小应该无关紧要,因为mySubview应该被限制在containerView中。
在MySubview.xib中我有3个对象,topButton:height = 29,middleView:height = 242和bottomButton:height = 29.(参见附图)TopButton具有前导,尾随和顶部到Superview约束和底部到中间视图约束。 MiddleView具有前导和尾随Superview约束以及top to topButton约束和bottom to bottomButton约束。最后,bottomButton具有前导,尾随和底部的Superview约束和top到middleView约束。
我想要发生的是mySubview扩展以适应containerView,因为已经创建并添加了约束,但是mySubview变得非常大并且剪辑了containerView。
以下是一些屏幕截图:
MyViewController.xib,标题下方的蓝色矩形是我的容器视图,并具有IBOutlet容器视图。
MySubview.xib
最后,结果是不正确的。
相反,我想要这个,我只是为了获得屏幕截图而伪造。
在iPhone 4上,
在iPhone 5上,
正如您在虚假屏幕截图中看到的那样,mySubview会缩放以适合containerView,即使containerView会缩放以适应不同的手机屏幕大小。
希望我没有过分关注这些信息。任何帮助都会很棒。我觉得我很接近,但必须错过一个关键步骤。 Grrrrr。
答案 0 :(得分:58)
有几点想法:
除了一些变量名称怪异之外,这基本上看起来很好:你的局部变量containerView
等于self.subView
,但你所有的其他行都引用了一个不同的变量,一个类财产,self.containerView
。您是否省略了设置containerView
类属性的行?但是当我修复它时,你的代码对我来说很好。
确保您在设置约束后不立即查看frame
,因为更改尚未反映在frame
设置中。如果您想强制它根据约束重新排除所有内容,您可以执行[containerView layoutIfNeeded];
。此外,如果您要确认frame
设置,最好在viewDidAppear
之后推迟查看这些值(即viewDidLoad
在视图构建过程中过早)。
对您的代码进行小调整(与您的问题无关),但是当我在容器视图中设置约束时,我不仅会设置NSLayoutAttributeTop
和NSLayoutAttributeLeading
和你一样,但NSLayoutAttributeBottom
和NSLayoutAttributeTrailing
(而不是NSLayoutAttributeWidth
和NSLayoutAttributeHeight
)。它完成了与代码相同的功能,但是当使用非零值时,它更直观。
无论如何,我只是做了以下代码,排列了你的代码,并且工作正常:
- (IBAction)didTouchUpInsideAddView:(id)sender
{
UIView *containerView = self.containerView;
UIView *newSubview = [[UIView alloc] initWithFrame:CGRectZero]; // initializing with CGRectZero so we can see it change
newSubview.translatesAutoresizingMaskIntoConstraints = NO;
newSubview.backgroundColor = [UIColor lightGrayColor];
[containerView addSubview:newSubview];
[containerView addConstraint:[NSLayoutConstraint constraintWithItem:newSubview
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:containerView
attribute:NSLayoutAttributeTop
multiplier:1.0
constant:0.0]];
[containerView addConstraint:[NSLayoutConstraint constraintWithItem:newSubview
attribute:NSLayoutAttributeLeading
relatedBy:NSLayoutRelationEqual
toItem:containerView
attribute:NSLayoutAttributeLeading
multiplier:1.0
constant:0.0]];
[containerView addConstraint:[NSLayoutConstraint constraintWithItem:newSubview
attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationEqual
toItem:containerView
attribute:NSLayoutAttributeBottom
multiplier:1.0
constant:0.0]];
[containerView addConstraint:[NSLayoutConstraint constraintWithItem:newSubview
attribute:NSLayoutAttributeTrailing
relatedBy:NSLayoutRelationEqual
toItem:containerView
attribute:NSLayoutAttributeTrailing
multiplier:1.0
constant:0.0]];
// the frame is still `CGRectZero` at this point
NSLog(@"newSubview.frame before = %@", NSStringFromCGRect(newSubview.frame));
// if not doing this in `viewDidLoad` (e.g. you're doing this in
// `viewDidAppear` or later), you can force `layoutIfNeeded` if you want
// to look at `frame` values. Generally you don't need to do this unless
// manually inspecting `frame` values or when changing constraints in a
// `animations` block of `animateWithDuration`.
[containerView layoutIfNeeded];
// everything is ok here
NSLog(@"containerView.bounds after = %@", NSStringFromCGRect(containerView.bounds));
NSLog(@"newSubview.frame after = %@", NSStringFromCGRect(newSubview.frame));
}
您可以使用visual format language简化该代码,例如:
NSDictionary *views = NSDictionaryOfVariableBindings(newSubview);
[containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[newSubview]|"
options:0
metrics:nil
views:views]];
[containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[newSubview]|"
options:0
metrics:nil
views:views]];
我发现使用可视化格式语言更容易获得约束。容易出错(至少对我来说)。但是,有些约束无法用视觉格式语言表示,在这种情况下,我会回到你概述的语法。
在您修改过的问题中,您向我们展示了您的子视图的init
方法,其中另一个 addSubview
。你也必须在那里设置约束。最重要的是,无论您在哪里addSubview
,都必须设置约束,例如
- (id)init
{
if(self = [super init])
{
NSArray *nibArray = [[NSBundle mainBundle]loadNibNamed:@"MySubview" owner:self options:nil];
UIView *subview = [nibArray objectAtIndex:0];
subview.translatesAutoresizingMaskIntoConstraints = NO;
[self addSubview:subview];
NSDictionary *views = NSDictionaryOfVariableBindings(subview);
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[subview]|"
options:0
metrics:nil
views:views]];
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[subview]|"
options:0
metrics:nil
views:views]];
}
return self;
}
答案 1 :(得分:1)
在问题中你说“我的新子视图没有缩放以匹配容器视图” - 但是根据描述我认为它们是 - 问题是你的容器视图没有固定的大小。
我认为如果您将容器视图设置为具有应该执行的固定宽度和高度,您可能还需要调用
[self.containerView layoutSubviews]
在更新约束后强制调整大小。
我还建议您更换为使用基于文本的格式,您可以将上面的行替换为“H:| [newSubview] |”和“V:| [newSubview] |”