iOS 8顶部布局指南自动布局问题

23
我的应用程序中的一个屏幕在更新到iOS 8 SDK时完全崩溃了。问题似乎是顶部布局指南向上移动,而不是成为其他视图的“锚点”。
以下是iOS 7中视图的外观:[图片]
以下是iOS 8中的外观(从Xcode 6 View Hierarchy调试器中捕获):[图片]
如您所见,该视图出现在导航栏上方很多。 关于顶部布局指南有两个约束条件,一个是上方图像视图,另一个是下面的白色视图。 底部布局指南没有约束条件,只有视图高度的约束条件。
由于某种原因,在第一次调用[self.view layoutIfNeeded]之后,iOS 8决定将顶部布局指南推到{0-255;0 0}。 此外,视图的边界有时出现过大的情况,即以统一的故事板尺寸(600x600),而不是320x480 / 320x568显示。
iOS 8有哪些更改可能会损坏布局?
[编辑] 这里是视图上的所有约束条件的完整列表:
(lldb) po self.view.constraints
<__NSArrayM 0x786ac520>(
    <NSLayoutConstraint:0x7c377bd0 V:[_UILayoutGuide:0x7c372f60]-(0)-[UIView:0x7c373120]>,
    <NSLayoutConstraint:0x7c377c00 UIView:0x7c373120.width == UIView:0x7c3730b0.width>,
    <NSLayoutConstraint:0x7c377c30 UIView:0x7c3730b0.centerX == UIView:0x7c373120.centerX>,
    <NSLayoutConstraint:0x7c377c60 H:|-(0)-[UIView:0x7c3716f0]   (Names: '|':UIView:0x7c3730b0 )>,
    <NSLayoutConstraint:0x7c377c90 UIView:0x7c3716f0.width == UIView:0x7c3730b0.width>,
    <NSLayoutConstraint:0x7c372f30 V:[_UILayoutGuide:0x7c372f60]-(129)-[UIView:0x7c3716f0]>
)

你解决了吗? - Mrunal
我知道这是一个很大的要求,但你能否发布所有约束条件的列表?有时它们会以奇怪的方式解决,如果没有看到它们中的所有内容,就很难跟踪发生了什么。 - Anna Dickinson
我在顶层视图上发布了所有约束的列表。我认为这应该足够了,因为整个视图都向上移动了... 如果您需要更多信息,请告诉我! - David Ganster
在视图的边界有时会出现很大的情况;在 viewDidLoad 中,边界仍将是故事板大小(通常为 600x600)。视图的边界将在 viewWillAppearviewDidAppear 中正确显示,因此请将您的框架/边界更新代码保存到这些方法中。 - keithbhunter
你是否已经提交了一个关于这个问题的错误报告? - lensovet
5个回答

43
默认情况下,在属性检查器的“扩展边缘”部分中,选中了“延伸边缘 > 在顶部栏下”。
尝试取消选中“在顶部栏下”。
这个方法在我遇到类似奇怪行为时帮助过我很多次。
请注意,这是一个UIViewController属性,所以您可以在代码中设置它。 在界面构建器中的在顶部栏下选项

干净的解决方案。这将限制主视图在导航栏下方。 - Greg T

5
你可以使用UIBarPositionTopAttached或者使用视图边界和框架,我也可以向你推荐并链接到苹果文档,这需要花费一些时间来解决问题。
最好、最简单的方法是将你的视图控制器直接嵌入一个导航控制器中。你只需选择视图控制器,然后转到编辑器 > 编辑为 > 导航控制器即可。(如果旧导航栏上有任何内容,可以先拖动它,将视图控制器嵌入导航控制器中,然后移动新导航栏上的按钮,最后删除旧导航栏)
或者,将导航栏的translucent属性设置为NO:
self.navigationController.navigationBar.translucent = NO;

这将修复视图被导航栏和状态栏框架的问题。
如果您需要显示和隐藏导航栏,则使用:
 if ([self respondsToSelector:@selector(edgesForExtendedLayout)])
    self.edgesForExtendedLayout = UIRectEdgeNone;   // iOS 7 specific

在您的viewDidLoad方法中。

希望这两个任意一个能够解决您的问题。


但是这个方法在自动布局中可行吗?根据我的经验,操作视图边界/框架通常会破坏自动布局。 - Anna Dickinson
很遗憾,你提供的建议都没有解决我的问题 :( - David Ganster
你的第二个建议正是我在AutoLayout中使用webView时所需要的。 self.navigationController?.navigationBar.isTranslucent = false 是Swift 3.1的语法。 - Jamie Birch
@JamieBirch 很高兴它对你有用,你可以请点赞这个答案吗? - Mrunal
@Mrunal 我做了 :/ - Jamie Birch

5
我正在使用Xcode 6.3.1,似乎该错误在该版本中重新出现了(据说在5.1中已经修复)。我在SO上找不到解决方法,所以不得不再搜索一些信息,这里是解决方法:
不要像平常在IB中使用Ctrl+拖动,只需选择要操作的视图并转到编辑器->固定->顶部空间至超级视图。
这是来源:LINK, 在我的情况下有所帮助。

2

我也遇到了这样的问题。如果仅在iOS8上运行,则可以将约束从“topLayoutGuide.bottom-myView.top”更改为“topLayoutGuide.top-myView.top”。但这会破坏iOS7的用户界面。虽然可以编写代码来支持两个版本,但在storyboards中创建UI时很难使UI两个版本都能正常工作。


1
自iOS 8以来,有一个新的布局属性:边距!请确保取消选择约束中的“margin”,以便在iOS 7下正常工作(从属性面板查看Storyboard中的约束时...)。 - Vinzzz
使用 "相对于边距" 选项是没有约束条件的。只有在 iPad 上,topLayoutGuide 突然有了 1068 的高度。这显然会导致视图放置得太低而超出屏幕。 - Nikita Ivaniushchenko
顺便说一下,我只在 iPad 横屏时遇到了这个问题。 - Nikita Ivaniushchenko
好的,我的问题已经解决了。我有一个下一个类别: @interface UIApplication(扩展) -(CGFloat)statusBarHeight; 这与内部iOS8 API发生了冲突。 - Nikita Ivaniushchenko
我在UIApplication的类别中有一个statusBarHeight方法,现在与同名的私有方法发生冲突。 - Nikita Ivaniushchenko
显示剩余2条评论

-4

不要使用统一的故事板(600x600),而应该为您的界面生成器保留其中一个设备分辨率,然后在 IB 中布局屏幕。

如果您的视图控制器在故事板中的尺寸为600x600,则在viewDidLoad中,它将为您的视图控制器的视图获取该框架并相应地布局子视图。

希望这可以帮助到你。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接