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

xib文件中的安全区域布局指南-iOS 10,ios,ios10,xcode9,iphone-x,Ios,Ios10,Xcode9,Iphone X,我开始为iPhoneX调整我的应用程序,并在Interface Builder中发现了一个问题。 根据苹果官方视频,安全区布局指南应该是向后兼容的。我发现它在故事板中工作得很好 但在我的XIB文件中,iOS 10不遵守安全区域布局指南 对于新的操作系统版本,它们工作正常,但iOS 10设备似乎只是简单地假设安全区域距离为零(忽略状态栏大小) 我是否缺少任何必需的配置?这是一个Xcode错误,如果是的话,有什么已知的解决方法吗 以下是测试项目中问题的屏幕截图(左iOS 10,右iOS 11): 安

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

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

对于新的操作系统版本,它们工作正常,但iOS 10设备似乎只是简单地假设安全区域距离为零(忽略状态栏大小)

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

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


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

您可能能够通过附加约束来解决这些问题,如superview.top的1000优先级>=20.0和750优先级==safearea.top。如果你总是显示一个状态栏,那应该可以解决问题

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

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

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


顺便问一下,你是如何显示你所看到的控制器的?它只是根视图控制器还是您提供了它,或者。。?我注意到的问题与推送视图控制器有关,因此,您可能遇到了另一种情况。

找到了最简单的解决方案-只需禁用
安全区域
,然后使用
topLayoutGuide
bottomLayoutGuide
+为iPhone X添加修复程序。这可能不是一个漂亮的解决方案,但需要尽可能少的努力。

我最终删除了对安全区域的约束,而我在我的xib文件中有。 相反,我创建了一个到所讨论的UIView的出口,并通过代码将其连接到ViewDidLayoutSubView中,如下所示

let constraint = alert.viewContents.topAnchor.constraint(equalTo: self.topLayoutGuide.bottomAnchor, constant: 0)
constraint.priority = 998
constraint.isActive = true
这会将一个小的“警报”绑定到屏幕顶部,但会确保警报中的内容视图始终位于顶部安全区域(IOS1ish)/topLayoutGuide(IOS1ish)的下方


简单且一次性的解决方案。如果出现故障,我将返回当前,向后兼容性不好

我的解决方案是在interface builder中创建两个约束,并根据您使用的ios版本删除一个约束:

  • 对于ios 11:
    view.top==安全区域.top
  • 对于早期版本:
    view.top==superview.top+20
将它们分别添加为
myConstraintsAferarea
myConstraintsPerview
两个门店。然后:

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

对我来说,一个简单的修复方法可以让它在两个版本上都工作

    if #available(iOS 11, *) {}
    else {
        self.edgesForExtendedLayout = []
    }
在文档中:“在iOS 10及更早版本中,使用此属性报告视图控制器的哪些边缘延伸到导航栏或其他系统提供的视图下方。”。 因此,将它们设置为空阵列可确保视图控制器不会延伸到导航栏下方

Docu可用

这也适用于:

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

我将本页中的一些答案组合到这篇文章中,这篇文章很有魅力(根据问题的要求,仅适用于顶部布局指南):

  • 确保在故事板或xib文件中使用安全区域
  • 将视图限制在安全区域
  • 对于每个
    视图
    ,其具有附加到SafeArea.top的
    约束
    • 视图创建IBOutlet
    • 约束创建一个IBOutler
  • 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;
    }
    
  • 编辑:

    这是我在我们的代码库中使用的改进版本。只需复制/粘贴下面的代码,并将每个视图和约束连接到它们的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;
        }
    }
    
    每个IBMoutletCollection的计数应相等。e、 g.对于每个视图 应该有它的关联约束


    我添加了一个NSLayoutConstraint子类来解决这个问题(
    IBAdjustableConstraint
    ),其中包含一个
    @IBInspectable
    变量,如下所示

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

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

    只需将其设置为IB中约束的子类,就可以进行我使用了这个,添加了顶部安全区域布局,并连接了插座

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

    我的具体问题只发生在XIB文件上,并且似乎发生在推送和呈现的控制器上。对于大多数情况,使用多个约束的变通方法听起来是一个很好的解决方案。谢谢我仍然希望他们能在iPhone到来之前解决这些问题。非常感谢您的优先建议!现在在iPhoneX下的iOS9和iOS11上运行起来就像一个符咒。我可以评论一下“如果你总是显示一个状态栏,那应该可以解决问题。”?我看不到如何在景观中显示iPhoneX模拟器上的状态栏。然后,所述的解决方案“1000优先级>=20.0到superview.top和750优先级==safearea.top”将在iPho顶部有20px的间隙