32

我在 IB 中有一个 UIView,其中有另一个 UIView,我将其用作容器视图。在代码中,我创建了三个不同的视图,然后根据应用程序的状态将适当的视图动画化到容器的视图中。在任何给定时间,这三种不同观点中只有一种是有效的。问题是,当我在模拟器中的不同 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]];  

我似乎无法让它发挥作用。我对自动布局还很陌生,不确定我做错了什么,我不想再把头撞在墙上了。任何帮助都会很棒。:)


************* 附加信息 **************


抱歉,我没有尽可能清楚地说明我的问题。因此,这里有更多关于屏幕截图的信息。首先,我将描述我在代码方面所做的事情。

在 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 中,我将笔尖添加为这样的子视图,

- (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。它有一个到 UIView* containerView 的 IBOutlet,是上面提到的那个。我对 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 约束。MiddleView 具有前导和尾随 Superview 约束以及从上到上按钮约束和从下到下按钮约束。最后,bottomButton 具有到 Superview 约束的前导、尾随和底部以及从顶部到 middleView 的约束。

我想要发生的是让 mySubview 缩放以适应 containerView,因为已经创建并添加了约束,但是相反,mySubview 变得非常大并剪辑了 containerView。

以下是一些屏幕截图:

MyViewController.xib,标题下方的蓝色矩形是我的容器视图,并且有 IBOutlet containerView。

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

我的子视图.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 会稍微缩放以适应不同的手机屏幕尺寸。

希望我没有过度使用这些信息。任何帮助都会很棒。我觉得我很接近,但肯定错过了一个关键步骤。呸呸呸。

4

2 回答 2

59

几个想法:

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

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

  3. 对您的代码稍作调整(与您的问题无关),但是当我在容器视图中设置约束时,我通常不仅会设置NSLayoutAttributeTopand NSLayoutAttributeLeading,就像您所做的那样,而且还会设置NSLayoutAttributeBottomand NSLayoutAttributeTrailing(而不是NSLayoutAttributeWidthand 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));
    }
    
  4. 您可以使用可视格式语言稍微简化该代码,例如:

    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;
    }
    
于 2013-04-22T23:23:57.193 回答
1

在问题中,您声明“我的新子视图未缩放以匹配容器视图” - 但从描述中我认为它们是 - 问题是您的容器视图没有固定大小。

我认为,如果您将容器视图设置为具有应该执行的固定宽度和高度,您可能还需要调用

[self.containerView layoutSubviews]

在更新约束后强制调整大小。

我还建议您改用基于文本的格式,您可以将上面的行换成“H:| [newSubview] |”之类的内容 和“V:|[newSubview]|”

于 2013-04-22T23:16:35.120 回答