【问题标题】:Safe area layout guides in xib files - iOS 10xib 文件中的安全区域布局指南 - iOS 10
【发布时间】:2018-02-28 02:53:06
【问题描述】:

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

但在我的 XIB 文件中,安全区域布局指南在 iOS 10 中不受尊重。

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

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

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

【问题讨论】:

    标签: ios ios10 xcode9 iphone-x


    【解决方案1】:

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

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

    【讨论】:

      【解决方案2】:

      我添加了一个 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 中约束的子类,您就可以进行

      【讨论】:

      • 你能帮我实现目标吗
      【解决方案3】:

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

      1. 确保在情节提要或 xib 文件中使用安全区域
      2. 将您的视野限制在安全区域内
      3. 对于每个view 都有一个constraint 附加到 SafeArea.top
        • view 创建一个 IBOutlet
        • constraint 创建一个 IBOutler
      4. viewDidLoad: 上的 ViewController 内

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

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

      【讨论】:

        【解决方案4】:

        这也有效:

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

        【讨论】:

          【解决方案5】:

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

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

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

          文档可用here

          【讨论】:

          • 不幸的是,这仅在导航栏可见时才有效。如果导航栏被隐藏,视图仍会在状态栏下方扩展edgesForExtendedLayout = []
          【解决方案6】:

          目前,向后兼容性不能很好地工作。

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

          • 对于 iOS 11:view.top == safe area.top
          • 对于早期版本:view.top == superview.top + 20

          将它们分别添加为myConstraintSAFEAREAmyConstraintSUPERVIEW 的出口。那么:

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

          【讨论】:

          • 这是一种比公认的答案更易于维护/更清洁的方法,imo。请注意,这确实取决于始终拥有状态栏
          • 我同意 - 一直在寻找答案,这似乎比为 iOS 10 和 iOS 11 拥有单独的 XIB 文件要干净得多。
          【解决方案7】:

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

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

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

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

          【讨论】:

          • 我认为这不是一个好主意,因为由于 viewDidLayoutSubviews 会在任何边界更改时调用,这将创建一个新约束并在每次边界更改时激活它。
          • 是的,您需要确保如果它存在,您不会创建另一个。
          【解决方案8】:

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

          【讨论】:

            【解决方案9】:

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

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

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

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

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

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

            【讨论】:

            • 我的具体问题仅在 XIB 文件上,并且似乎发生在推送和呈现的控制器上。对于大多数情况,具有多个约束的解决方法听起来像是一个很好的解决方法。谢谢!我仍然希望他们能在 iPhoneX 到来之前解决这些问题。
            • 非常感谢您的优先建议!现在可以在 iPhone X 下的 iOS 9 和 iOS 11 上运行。
            • 我可以评论一下这个“如果你总是显示状态栏,那应该可以解决问题。”?我看不到在 iPhone X 模拟器上横向显示状态栏的方式。然后,所描述的“1000 优先级 >= 20.0 到 superview.top 和 750 优先级 == safearea.top”的解决方法对于 iPhone X 在横向上将有 20px 的间隙。如果 iOS = 20.0”的约束。我是否错过了在 iPhone X 横向中强制状态栏的方法?
            • 这是一个很好的观点。我只处理我正在使用的应用程序的肖像。对于 iOS 11,无论如何都没有理由有这个限制。这听起来像是一个很好的解决方案,或者以编程方式为 = 20 一个。
            • Apple 应该为 ios @clarus 提供向后兼容性,或者它可能包含在 wwdc 2018 中,而不是给予双重约束?
            猜你喜欢
            • 2018-03-02
            • 1970-01-01
            • 1970-01-01
            • 2018-06-26
            • 1970-01-01
            • 1970-01-01
            • 2018-06-06
            • 1970-01-01
            相关资源
            最近更新 更多