使用autolayout将动态UIView设置为与容器视图匹配

时间:2013-04-22 22:16:26

标签: ios uiview autolayout resize

我在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容器视图。

http://i33.tinypic.com/14o3lmd.png

MySubview.xib

http://i37.tinypic.com/344ukk3.png

最后,结果是不正确的。

http://i34.tinypic.com/nbyqua.png

相反,我想要这个,我只是为了获得屏幕截图而伪造。

在iPhone 4上,

http://tinypic.com/r/w9fp5e/4

在iPhone 5上,

http://tinypic.com/r/2z8nldv/4

正如您在虚假屏幕截图中看到的那样,mySubview会缩放以适合containerView,即使containerView会缩放以适应不同的手机屏幕大小。

希望我没有过分关注这些信息。任何帮助都会很棒。我觉得我很接近,但必须错过一个关键步骤。 Grrrrr。

2 个答案:

答案 0 :(得分:58)

有几点想法:

  1. 除了一些变量名称怪异之外,这基本上看起来很好:你的局部变量containerView等于self.subView,但你所有的其他行都引用了一个不同的变量,一个类财产,self.containerView。您是否省略了设置containerView类属性的行?但是当我修复它时,你的代码对我来说很好。

  2. 确保您在设置约束后不立即查看frame,因为更改尚未反映在frame设置中。如果您想强制它根据约束重新排除所有内容,您可以执行[containerView layoutIfNeeded];。此外,如果您要确认frame设置,最好在viewDidAppear之后推迟查看这些值(即viewDidLoad在视图构建过程中过早)。

  3. 对您的代码进行小调整(与您的问题无关),但是当我在容器视图中设置约束时,我不仅会设置NSLayoutAttributeTopNSLayoutAttributeLeading和你一样,但NSLayoutAttributeBottomNSLayoutAttributeTrailing(而不是NSLayoutAttributeWidthNSLayoutAttributeHeight)。它完成了与代码相同的功能,但是当使用非零值时,它更直观。

    无论如何,我只是做了以下代码,排列了你的代码,并且工作正常:

    - (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));
    }
    
  4. 您可以使用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]];
    

    我发现使用可视化格式语言更容易获得约束。容易出错(至少对我来说)。但是,有些约束无法用视觉格式语言表示,在这种情况下,我会回到你概述的语法。

  5. 在您修改过的问题中,您向我们展示了您的子视图的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] |”