xib 文件中的安全区域布局指南 - iOS 10

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/46298427/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-31 09:29:04  来源:igfitidea点击:

Safe area layout guides in xib files - iOS 10

iosios10xcode9iphone-x

提问by Tiago Lira

I started adapting my app for iPhone X and found an issue in Interface Builder. The safe area layout guides are supposed to be backwards compatible, according to official Apple videos. I found that it works just fine in storyboards.

我开始为 iPhone X 调整我的应用程序,并在 Interface Builder 中发现了一个问题。根据 Apple 官方视频,安全区域布局指南应该向后兼容。我发现它在故事板中工作得很好。

But in my XIB files, the safe area layout guides are not respected in iOS 10.

但是在我的 XIB 文件中,iOS 10 中不遵守安全区域布局指南。

They work fine for the new OS version, but the iOS 10 devices seem to simply assume the safe area distance as zero (ignoring the status bar size).

它们适用于新的操作系统版本,但 iOS 10 设备似乎只是假设安全区域距离为零(忽略状态栏大小)。

Am I missing any required configuration? Is it an Xcode bug, and if so, any known workarounds?

我是否缺少任何必需的配置?这是一个 Xcode 错误,如果是,是否有任何已知的解决方法?

Here is a screenshot of the issue in a test project (left iOS 10, right iOS 11):

这是测试项目中问题的屏幕截图(左 iOS 10,右 iOS 11):

safe area alignment on top of the screen

屏幕顶部的安全区域对齐

回答by clarus

There are some issues with safe area layout and backwards compatibility. See my comment over here.

安全区域布局和向后兼容性存在一些问题。在这里看到我的评论。

You might be able to work around the issues with additional constraints like a 1000 priority >= 20.0 to superview.top and a 750 priority == safearea.top. If you always show a status bar, that should fix things.

您也许可以通过额外的约束来解决这些问题,比如 1000 优先级 >= 20.0 到 superview.top 和 750 优先级 == safearea.top。如果您总是显示状态栏,那应该可以解决问题。

A better approach may be to have separate storyboards/xibs for pre-iOS 11 and iOS-11 and up, especially if you run into more issues than this. The reason that's preferable is because pre-iOS 11 you should layout constraints to the top/bottom layout guides, but for iOS 11 you should lay them out to safe areas. Layout guides are gone. Laying out to layout guides for pre-iOS 11 is stylistically better than just offsetting by a min of 20 pixels, even though the results will be the same IFF you always show a status bar.

更好的方法可能是为 iOS 11 之前和 iOS-11 及更高版本使用单独的故事板/xib,尤其是当您遇到比这更多的问题时。之所以更可取,是因为在 iOS 11 之前,您应该对顶部/底部布局指南进行布局约束,但对于 iOS 11,您应该将它们布置到安全区域。布局指南不见了。布局到 iOS 11 之前的布局指南在风格上比仅仅偏移 20 像素更好,即使结果将与您始终显示状态栏的 IFF 相同。

If you take this approach, you'll need to set each file to the correct deployment target that it will be used on (iOS 11, or something earlier) so that Xcode doesn't give you warnings and allows you to use layout guides or safe areas, depending. In your code, check for iOS 11 at runtime and then load the appropriate storyboard/xibs.

如果您采用这种方法,您需要将每个文件设置为将在其上使用的正确部署目标(iOS 11 或更早版本),以便 Xcode 不会向您发出警告并允许您使用布局指南或安全区域,视情况而定。在您的代码中,在运行时检查 iOS 11,然后加载适当的故事板/xib。

The downside of this approach is maintenance, (you'll have two sets of your view controllers to maintain and keep in sync), but once your app only supports iOS 11+ or Apple fixes the backward compatibility layout guide constraint generation, you can get rid of the pre-iOS 11 versions.

这种方法的缺点是维护,(您将有两套视图控制器来维护和保持同步),但是一旦您的应用程序仅支持 iOS 11+ 或 Apple 修复了向后兼容性布局指南约束生成,您就可以获得摆脱 iOS 11 之前的版本。

By the way, how are you displaying the controller that you're seeing this with? Is it just the root view controller or did you present it, or..? The issue I noticed has to do with pushing view controllers, so you may be hitting a different case.

顺便说一句,你如何显示你看到这个的控制器?它只是根视图控制器还是您提供了它,或者..?我注意到的问题与推送视图控制器有关,因此您可能会遇到不同的情况。

回答by Martin Massera

Currently, backward compatibility doesn't work well.

目前,向后兼容性效果不佳。

My solution is to create 2 constraints in interface builder and remove one depending on the ios version you are using:

我的解决方案是在界面构建器中创建 2 个约束并根据您使用的 ios 版本删除一个:

  • for ios 11: view.top == safe area.top
  • for earlier versions: view.top == superview.top + 20
  • 对于 ios 11: view.top == safe area.top
  • 对于早期版本: view.top == superview.top + 20

Add them both as outlets as myConstraintSAFEAREAand myConstraintSUPERVIEWrespectively. Then:

将它们分别添加为出口 asmyConstraintSAFEAREAmyConstraintSUPERVIEW。然后:

override func viewDidLoad() {
    if #available(iOS 11.0, *) {
        view.removeConstraint(myConstraintSUPERVIEW)
    } else {
        view.removeConstraint(myConstraintSAFEAREA)
    }
}

回答by Falco Winkler

For me, a simple fix for getting it to work on both versions was

对我来说,让它在两个版本上都能工作的简单修复是

    if #available(iOS 11, *) {}
    else {
        self.edgesForExtendedLayout = []
    }

From the documentation: "In iOS 10 and earlier, use this property to report which edges of your view controller extend underneath navigation bars or other system-provided views. ". So setting them to an empty array makes sure the view controller does notextend underneath nav bars.

来自文档:“在 iOS 10 及更早版本中,使用此属性报告视图控制器的哪些边缘在导航栏或其他系统提供的视图下方延伸。”。所以将它们设置为空数组可以确保视图控制器并不能延长下方的导航栏。

Docu is available here

Docu 可在此处获得

回答by Tumata

I have combined some of the answers from this page into this, which works like a charm (only for top layout guide, as requested in the question):

我已将此页面中的一些答案合并到此中,这就像一个魅力(仅适用于问题中的要求的顶部布局指南):

  1. Make sure to use safe area in your storyboard or xib file
  2. Constraint your views to the safe areas
  3. For each viewwhich has a constraintattached to the SafeArea.top
    • Create an IBOutlet for the view
    • Create an IBOutler for the constraint
  4. Inside the ViewController on viewDidLoad:

    if (@available(iOS 11.0, *)) {}
    else {
        // For each view and constraint do:
        [self.view.topAnchor constraintEqualToAnchor:self.topLayoutGuide.bottomAnchor].active = YES;
        self.constraint.active = NO;
    }
    
  1. 确保在故事板或 xib 文件中使用安全区域
  2. 将您的视线限制在安全区域
  3. 对于每一个view其中有一个constraint附接至SafeArea.top
    • 创建一个IBOutlet view
    • constraint
  4. 在 ViewController 里面 viewDidLoad:

    if (@available(iOS 11.0, *)) {}
    else {
        // For each view and constraint do:
        [self.view.topAnchor constraintEqualToAnchor:self.topLayoutGuide.bottomAnchor].active = YES;
        self.constraint.active = NO;
    }
    

Edit:

编辑:

Here is the improved version I ended up using in our codebase. Simply copy/paste the code below and connect each view and constraints to their IBOutletCollection.

这是我最终在我们的代码库中使用的改进版本。只需复制/粘贴下面的代码并将每个视图和约束连接到它们的 IBOutletCollection。

@property (strong, nonatomic) IBOutletCollection(NSLayoutConstraint) NSArray *constraintsAttachedToSafeAreaTop;
@property (strong, nonatomic) IBOutletCollection(UIView) NSArray *viewsAttachedToSafeAreaTop;


if (@available(iOS 11.0, *)) {}
else {
    for (UIView *viewAttachedToSafeAreaTop in self.viewsAttachedToSafeAreaTop) {
        [viewAttachedToSafeAreaTop.topAnchor constraintEqualToAnchor:self.topLayoutGuide.bottomAnchor].active = YES;
    }
    for (NSLayoutConstraint *constraintAttachedToSafeAreaTop in self.constraintsAttachedToSafeAreaTop) {
        constraintAttachedToSafeAreaTop.active = NO;
    }
}

The count of each IBOutletCollection should be equal. e.g. for each view there should be its associated constraint

每个 IBOutletCollection 的计数应该相等。例如对于每个视图都应该有其关联的约束

回答by Alex Brown

I added a NSLayoutConstraint subclass to fix this problem (IBAdjustableConstraint), with a @IBInspectablevariable, looks like this.

我添加了一个 NSLayoutConstraint 子类来解决这个问题(IBAdjustableConstraint),一个@IBInspectable变量,看起来像这样。

class IBAdjustableConstraint: NSLayoutConstraint {

    @IBInspectable var safeAreaAdjustedConstant: CGFloat = 0 {
        didSet {
            if OS.TenOrBelow {
                constant += safeAreaAdjustedConstantLegacy
            }
        }
    }
}

And OS.TenOrBelow

OS.TenOrBelow

struct OS {
    static let TenOrBelow = UIDevice.current.systemVersion.compare("10.9", options: NSString.CompareOptions.numeric) == ComparisonResult.orderedAscending
}

Just set that as the subclass of your constraint in IB and you will be able to make < iOS11 specific changes. Hope this helps someone.

只需将其设置为 IB 中约束的子类,您就可以进行 < iOS11 特定更改。希望这可以帮助某人。

回答by little

I used this one, add the top safe area layout and connect with outlet

我用了这个,添加顶部安全区布局并连接插座

@IBOutlet weak var topConstraint : NSLayoutConstraint!

override func viewDidLoad() {
    super.viewDidLoad()
    if !DeviceType.IS_IPHONE_X {
        if #available(iOS 11, *)  {
        }
        else{
            topConstraint.constant = 20
        }
    }
}

回答by Menan Vadivel

This also works:

这也有效:

override func viewDidLoad() {
    super.viewDidLoad()

    if #available(iOS 11.0, *) {}
    else {
        view.heightAnchor.constraint(equalToConstant: UIScreen.main.bounds.height - 80).isActive = true
        view.widthAnchor.constraint(equalToConstant: UIScreen.main.bounds.width - 20).isActive = true
    }
}

回答by Jonny

I ended up deleting the constraint to safe area which I had in my xib file. Instead I made an outlet to the UIView in question, and from code I hooked it up like this, in viewDidLayoutSubviews.

我最终删除了我的 xib 文件中对安全区域的约束。相反,我为有问题的 UIView 做了一个出口,并从代码中将它连接起来,在 viewDidLayoutSubviews 中。

let constraint = alert.viewContents.topAnchor.constraint(equalTo: self.topLayoutGuide.bottomAnchor, constant: 0)
constraint.priority = 998
constraint.isActive = true

This ties a small "alert" to top of screen but makes sure that the contents view within the alert is always below the top safe area(iOS11ish)/topLayoutGuide(iOS10ish)

这将一个小的“警报”绑定到屏幕顶部,但确保警报中的内容视图始终低于顶部安全区域(iOS11ish)/topLayoutGuide(iOS10ish)

Simple and a one-off solution. If something breaks, I'll be back .

简单且一次性的解决方案。如果有什么坏事,我会回来的。

回答by Vyachaslav Gerchicov

Found the simpliest solution - just disable safe areaand use topLayoutGuideand bottomLayoutGuide+ add fixes for iPhone X. Maybe it is not beautiful solution but requires as less efforts as possible

找到最简单的解决方案 - 只需禁用safe area和使用topLayoutGuidebottomLayoutGuide为 iPhone X 添加修复程序。 也许这不是漂亮的解决方案,但需要尽可能少的努力