简体   繁体   English

Xcode自动布局约束混乱

[英]Xcode Autolayout Constraints Confusion

I have a landscape based view that has a SKView ( skView ) inside of it. 我有一个基于景观的视图,其中包含一个SKView( skView )。 I want the width of skView to always be 100% of the view and a static height of 288. I have added some constraints but when I start the application in the simulator it ways gives me the same CGSize (568 x 288), regardless of if I pick iPhone 4 or 3.5. 我希望skView的宽度始终是视图的100%,静态高度为288。我添加了一些约束,但是当我在模拟器中启动应用程序时,无论如何,它都会给我相同的CGSize(568 x 288)。如果我选择iPhone 4或3.5。

- (void)viewDidLoad {
    CGSize size = self.skView.bounds.size; // 568 x 288
    ...
}

What am I missing here? 我在这里想念什么?

I've set up a sample project to illustrate the issue at hand https://github.com/kyledecot/autolayout 我已经建立了一个示例项目来说明手头的问题https://github.com/kyledecot/autolayout

在此处输入图片说明

Update #1 更新#1

One user suggested that I see what the value of self.skView.bounds.size is in viewWillAppear: which yielded some strange results (I'm not really sure if they're helpful) 一位用户建议我看看self.skView.bounds.size的值在viewWillAppear:是什么viewWillAppear:产生了一些奇怪的结果(我不确定它们是否有帮助)

在此处输入图片说明

Update #2 更新#2

After removing all constraints and re-adding them as one commenter (updated in the github sample project I posted) suggested I now have the following (still broken though): 删除所有约束并将其重新添加为一个评论者(在我发布的github示例项目中更新)后,建议我现在有以下内容(尽管仍然无效):

在此处输入图片说明

The lines in IB are blue which I means there is no ambiguity but it still doesn't seem to be working. IB中的线是蓝色的,这意味着没有歧义,但似乎仍然无法正常工作。

Update #3 更新#3

After receiving new constraints from user matt and using viewWillAppear instead of viewDidLoad the problem still seems to be prevalent. 从用户matt接收到新的约束并使用viewWillAppear而不是viewDidLoad ,问题似乎仍然很普遍。 It seems that skView 's width never gets conformed to that of view 看来skView的宽度永远不会符合view的宽度

在此处输入图片说明

As you can see in the screenshot the text is cut off because on a 3.5 inch iPhone skView still has the width of 568 (iPhone 4 inch's width in landscape). 如您在屏幕快照中所见,文本被截断了,因为在3.5英寸的iPhone上, skView的宽度仍然为568(横向上iPhone的宽度为4英寸)。

It sounds like the constraints of the view haven't been resolved yet when your view configuration code kicks in. That is, the frames at that time do not correspond the "real" layout that you have created using auto layout constraints (which should fit the skView to the view ). 听起来好像在启动视图配置代码时尚未解决视图的约束。也就是说,那时的框架与使用自动布局约束创建的“实际”布局不符(应该适合skViewview )。

I have moved the code from viewWillAppear to viewDidLayoutSubviews instead and it turns out that, at this time of the layout process, the bounds of your view and skView have been updated and it seems to work for both 3.5" and 4" screens. 我将代码从viewWillAppear移到了viewDidLayoutSubviews ,事实证明,在布局过程中,您的viewskView的边界已更新,并且似乎适用于3.5英寸和4英寸的屏幕。

- (void)viewDidLayoutSubviews {
    [super viewDidLayoutSubviews];

    CGSize skViewSize = self.skView.bounds.size;
    SKScene *scene = [MyScene sceneWithSize:skViewSize];
    scene.scaleMode = SKSceneScaleModeAspectFill;

    [self.skView presentScene:scene];
}

This tutorial might shed some more light on the topic. 本教程可能会进一步阐明该主题。

Btw, I have submitted a pull request with my changes to your sample repository so you can browse the change. 顺便说一句,我已经向您的示例存储库提交了包含我的更改的拉取请求,以便您可以浏览更改。

viewWillAppear and viewDidLoad are called before the constraints are handled in view controller. 在视图控制器中处理约束之前,先调用viewWillAppearviewDidLoad
If your views rely on the size only when the view is created (eg presentScene in your code) then you are better do it in viewDidAppear or in 'viewDidLayoutSubviews'. 如果您的视图仅在创建视图时才依赖于大小(例如,代码中的presentScene),则最好在viewDidAppear或“ viewDidLayoutSubviews”中执行此操作。
I have tried your code in each of these methods (after commenting out the viewWillAppear ) and it works as expected. 我已经在每种方法中尝试了您的代码(在注释掉viewWillAppear ),它可以按预期工作。

EDIT 编辑
I have changed the scaleMode in wiewWillAppear from SKSceneScaleModeAspectFill to SKSceneScaleModeFill and it works good as well. 我已经scaleMode wiewWillAppearscaleModeSKSceneScaleModeAspectFillSKSceneScaleModeFill ,它也很好用。
I think that this is better approach than presenting the scene only after the view controller is presented. 我认为这比仅在呈现视图控制器之后呈现场景更好的方法。

我认为有一种捷径方法。.首先,您必须在File Inspector中选中“ Use Auto Layout”,然后在界面生成器中显示四个按钮:1:align 2:Pin 3:Reslove autolayout问题和4:调整大小的行为..现在选择任何对象,要在横向和纵向模式下显示相同的对象,然后单击第三个按钮(Reslove自动布局)..选择“在视图控制器中添加缺少的约束”或“在视图控制器中添加缺少的约束”。查看”。

There are two problems here. 这里有两个问题。

First, your original constraints were completely wrong; 首先,您最初的限制是完全错误的; you do not want a vertical space from the top and bottom of the SKView if you also want the vertical height to be fixed! 如果您还希望固定垂直高度,则不需要SKView顶部和底部的垂直空间! Those two things are opposites of each other. 这两件事是彼此对立的。

Second, there's a bug in Xcode. 其次,Xcode中存在一个错误。 :( You can see it in your second set of constraints. The top constraint to the Top Layout Guide has come out wrong (you can see that from the -288). You have accidentally formed a constraint from the Top Layout Guide to the bottom of the SKView (that's the Xcode bug, it does that to you). Here's how to work around the bug: :(你可以看到它在你的第二组约束,顶部约束到顶部布局指南已经走出了错误的(你可以看到,从-288)。你不小心形成了从顶部布局指南制约底部 SKView的代码(这是Xcode的错误,它会对您执行此操作)。以下是解决该错误的方法:

  • Delete the top constraint. 删除最高约束。

  • Move the SKView down the screen. 将SKView向下移动到屏幕上。

  • Control-drag to form the top constraint to the Top Layout Guide again. 按住Control并再次拖动以形成“顶部布局指南”的顶部约束。

  • Now select the top constraint and manually set its Constant to 0, to make the SKView move back up again. 现在选择顶部约束,并将其常量手动设置为0,以使SKView再次向上移动。

The result will be: 结果将是:

  • SKView has a height constraint of 280. SKView的高度限制为280。

  • View (the main view) has a leading and following horizontal space constraint of 0 to the SKView, and a top constraint of 0 (not -288!) from the Top Layout Guide to the SKView. 视图(主视图)对SKView的领先水平空间约束为0,对于SKView的顶部约束为0(不是-288!)。

Bingo, now everything will work correctly. 宾果游戏,现在一切正常。

Actually i don't known the correct reason but put call layoutIfNeeded method and after it will show the currect size. 实际上,我不知道正确的原因,但是放了call layoutIfNeeded方法,之后它会显示当前的大小。

[self.skView layoutIfNeeded];
CGSize size = self.skView.bounds.size;

Inspired by @Michael Kessler's answer I've done some experiments. 受@Michael Kessler的回答启发,我做了一些实验。

In viewDidAppear I logged the sizes of the main view, skView and the scene. viewDidAppear我记录了主视图,skView和场景的大小。 For different scaleModes here are the results. 对于不同的scaleMode,结果如下。

SKSceneScaleModeAspectFill SKSceneScaleModeAspectFill

viewSize = {480, 320}, skViewSize = {480, 280}, sceneSize = {568, 280} viewSize = {480,320},skViewSize = {480,280},sceneSize = {568,280}

SKSceneScaleModeFill SKSceneScaleModeFill

viewSize = {480, 320}, skViewSize = {480, 280}, sceneSize = {568, 280} viewSize = {480,320},skViewSize = {480,280},sceneSize = {568,280}

SKSceneScaleModeResizeFill SKSceneScaleModeResizeFill

viewSize = {480, 320}, skViewSize = {480, 280}, sceneSize = {480, 280} viewSize = {480,320},skViewSize = {480,280},sceneSize = {480,280}

So clearly the mode you want is SKSceneScaleModeResizeFill to make the scene size match exactly the view size. 显然,您想要的模式是SKSceneScaleModeResizeFill,以使场景大小与视图大小完全匹配。

You will note however that Hello, World is still off the right side of the screen. 但是您会注意到,“ Hello,World”仍然位于屏幕右侧。 The reason is that the position of the SKLabel is set when the SKScene is initialised which is in viewDidAppear when the scene size is the incorrect width. 原因是初始化SKScene时设置了SKLabel的位置,当场景大小不正确的宽度时,该位置在viewDidAppear

You can reposition it as follows in your MyScene class. 您可以按照以下步骤在MyScene类中重新定位它。 When the SKScene is resized this method is called. 调整SKScene的大小时,将调用此方法。

-(void)didChangeSize:(CGSize)oldSize {
    _myLabel.position = CGPointMake(self.size.width, self.size.height/2.0);
}

NOTE: Things appear to work with SKSceneScaleModeFill but what you have there is a scene of width 568, scaled to fit in 480 rather than a scene of width 480. 注意:事情似乎可以与SKSceneScaleModeFill一起使用,但是您所拥有的是一个宽度为568的场景,缩放以适合480,而不是一个宽度为480的场景。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM