使用自动布局设置动态 UIView 以匹配容器视图 [英] Use autolayout to set dynamic UIView to match container view

查看:20
本文介绍了使用自动布局设置动态 UIView 以匹配容器视图的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在 IB 中有一个 UIView,其中有另一个 UIView,我将其用作容器视图.在代码中,我创建了三个不同的视图,然后根据应用程序的状态将适当的视图动画化到容器的视图中.在任何给定时间,三种不同的视图中只有一种是有效的.问题是,当我在模拟器中的不同 iPhone 上运行时,我的新子视图没有缩放以匹配容器视图.我正在使用自动布局.出于测试目的,我将我的子视图设置为一个大按钮,它的所有边缘都限制在超级视图上.并且容器视图的边缘也被限制在它的超级视图上.我想要的是子视图与容器的视图相匹配.即按钮拉伸容器视图的整个大小.当在不同的 iPhone 上运行时,容器视图和子视图的大小应该相对于不同 iPhone 的屏幕尺寸按比例缩放.

I have a UIView in IB that has another UIView within it, which I am using as a container view. In code, I create three different views and then animate the appropriate one into the container's view depending on the state of the app. Only one of the three different views will be valid at any given time. The problem is, when I run on different iPhone's in the simulator, my new subviews are not scaled to match the container view. I am using autolayout. For testing purposes, I've set up my subviews to just be a big button that has all it's edges constrained to the superview. And the container view also has it's edges constrained to it's superview. What I want is for the subview to match the container's view. i.e the button stretches the entire size of the container view. When run on different iPhones the size of the container view and therefore the subview should scale proportionally relative to the different iPhone's screen sizes.

下面是我用来初始化我的子视图并设置它相对于容器视图的约束的代码.

Below is the code I use to init my subview and set up it's constraints relative to the container view.

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]];  

我似乎无法让它发挥作用.我对自动布局还很陌生,不确定我做错了什么,并希望停止用头撞墙.任何帮助都会很棒.:)

I just can't seem to get this to work. I'm fairly new to autolayout and am not sure what I am doing wrong and would like to stop banging my head against this wall. Any help would be terrific. :)

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

************* ADDITIONAL INFO **************

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

Sorry, I've not stated my problem as clearly as I could have. So here is some more info with screen shots. First, I'll describe what I've done code-wise.

在 AppDelegate.m 的 didFinishLaunchingWithOptions 中,我像这样创建了 MyViewController,

In didFinishLaunchingWithOptions in AppDelegate.m, I create MyViewController like this,

self.myViewController = [[MyViewController alloc] initWithNibName:@"MyViewController" bundle:nil];

在 MyViewController.m 的 viewDidLoad 中,我创建了 mySubview 并将其添加到我的 containerView 并像这样为它创建约束,

In viewDidLoad in MyViewController.m, I create mySubview and add it to my containerView and create constraints for it like this,

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

And finally in init in MySubview.h I add the nib as a subview like this,

- (id)init
{
    if(self = [super init])
    {
        NSArray *nibArray = [[NSBundle mainBundle]loadNibNamed:@"MySubview" owner:self options:nil];
        [self addSubview:[nibArray objectAtIndex:0]];
    }
    return self;
}

需要注意的几件事可能会有所帮助,

A couple of things to note that may help,

在 MyViewController.xib 中,我有一个用作容器视图的 UIView.它有一个到 UIView* containerView 的 IBOutlet,就是上面提到的那个.我对 IB 中的 containerView 的唯一限制是,Superview 的前导、尾随和底部空间以及 Superview 的顶部空间 = 44.

In MyViewController.xib, I have a UIView that I am using as the containerView. It has an IBOutlet to UIView* containerView and is the one referenced above. The only constraints I have for the containerView in IB are, leading, trailing and bottom space to Superview and top space to Superview = 44.

对于 MySubview.xib,高度和宽度为 300,(高度或宽度不使用约束).我觉得这些大小应该无关紧要,因为 mySubview 应该被限制为 containerView.

For MySubview.xib, the height and width are 300, (no constraints are used for height or width). I feel like these sizes should not matter since mySubview is supposed to get constrained to containerView.

在 MySubview.xib 中,我有 3 个对象,topButton: height = 29,middleView: height = 242 和 bottomButton: height = 29.(见附图)TopButton 具有前导、尾随和顶部到 Superview 的约束以及底部到 middleView约束.MiddleView 具有前导和尾随到 Superview 的约束以及一个从顶部到顶部按钮的约束以及一个从底部到底部按钮的约束.最后,bottomButton 具有前导、尾随和底部到 Superview 的约束以及一个从顶部到 middleView 的约束.

In MySubview.xib I have 3 objects, topButton: height = 29, middleView: height = 242 and bottomButton: height = 29. (see attached image) TopButton has leading, trailing and top to Superview constraints and a bottom to middleView constraint. MiddleView has leading and trailing to Superview constraints and a top to topButton constraint and a bottom to bottomButton constraints. And finally bottomButton has leading, trailing and bottom to Superview constraints and a top to middleView constraint.

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

What I want to happen is for mySubview to scale to fit the containerView since constraints have been create and added but instead, mySubview gets very large and clips containerView.

以下是一些屏幕截图:

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

MyViewController.xib, the blue rectangle below the title is my container view and has the IBOutlet containerView.

MySubview.xib

MySubview.xib

最后,结果是不正确的.

And finally, the result, which are incorrect.

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

Instead I would want this, which I have faked just to get the screen shot.

在 iPhone 4 上,

On iPhone 4,

在 iPhone 5 上,

On iPhone 5,

正如你在假屏幕截图中看到的,mySubview 会缩放以适应 containerView,即使 containerView 会缩放以适应不同的手机屏幕尺寸.

As you can see in the fake screen shots, mySubview scales to fit the containerView, even as containerView scales a bit to adjust for the different phone screen sizes.

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

Hope I did not go overboard with the info. Any help would be terrific. I feel like I am close but must be missing one key step. Grrrrr.

推荐答案

一些想法:

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

  1. This basically looks fine, other than some variable name weirdness: Your local variable, containerView is equal to self.subView, but all of your other lines refer to a different variable, a class property, self.containerView. Did you omit a line where you set that containerView class property? But when I fixed that, your code worked fine for me.

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

Make sure you're not trying to look at the frame immediately after setting the constraints, as the change will not yet be reflected in the frame settings. You can do a [containerView layoutIfNeeded]; if you want to force it to relayout everything based upon the constraints. Also, if you want to confirm frame settings, it's better to defer looking at those values until after viewDidAppear (i.e. viewDidLoad is too early in the view construction process).

对您的代码稍作调整(与您的问题无关),但是当我在容器视图中设置约束时,我通常不仅会设置 NSLayoutAttributeTopNSLayoutAttributeLeading,和你一样,还有 NSLayoutAttributeBottomNSLayoutAttributeTrailing(而不是 NSLayoutAttributeWidthNSLayoutAttributeHeight).它完成与您的代码相同的事情,但在使用非零值时,它更直观.

A minor tweak on your code (and unrelated to your problem), but when I'm setting the constraints within a container view, I often will set not only NSLayoutAttributeTop and NSLayoutAttributeLeading, like you did, but also NSLayoutAttributeBottom and NSLayoutAttributeTrailing (rather than NSLayoutAttributeWidth and NSLayoutAttributeHeight). It accomplishes the same thing as your code, but when using non-zero values, it is a fraction more intuitive.

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

Anyway, I just did the following code, permutation of yours, and it works fine:

- (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));
}

  • 您可以使用 视觉格式语言,例如:

    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]];
    

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

    I find it easier to get the constraints right using visual format language. A little less error-prone (for me, at least). There are, though, some constraints that cannot be represented in visual format language, in which case I fall back to the syntax you outline.

    在您修改后的问题中,您向我们展示了子视图的 init 方法,该方法执行另一个 addSubview.你也必须在那里设置约束.底线,无论你做什么 addSubview,你都必须设置你的约束,例如

    In your revised question, you show us an init method for your subview, which does another addSubview. You have to set constraints there, too. Bottom line, wherever you do addSubview, you have to set your constraints, e.g.

    - (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;
    }
    

  • 这篇关于使用自动布局设置动态 UIView 以匹配容器视图的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆