【问题标题】:Creating a UIButton programmatically on iPhone X but safe area issue在 iPhone X 上以编程方式创建 UIButton 但安全区域问题
【发布时间】:2017-10-14 00:17:19
【问题描述】:

我在TabBarController 中间创建了一个UIButton,但由于其位于手机底部的安全区域,它在 iPhone X 上无法正确显示。

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
    //Frame mic button
    micButton.frame = CGRect.init(x: self.tabBar.center.x - 62, y: self.view.bounds.height - 94, width: 124, height: 124)
    micButton.layer.cornerRadius = 62        
}

在不触及安全区域的情况下绘制此UIButton 的正确方法是什么。

【问题讨论】:

  • 您发布的代码与任何与自动布局相关的内容都完全不同。 (1) 为什么要设置帧值?这可能是重要的事情。 (2) 安全区域通常——甚至可能绝对——意味着约束。他们在哪?我的意思是问... (3) iPhone X 的“安全区域”——顾名思义,iOS 11——是指safeAreaLayoutGuide。那么为什么要显示不使用此或任何其他自动布局约束的代码?
  • @dfd 没错,但如果您知道一个很好的编程约束指南或教程,我从来没有以编程方式进行布局。另外,我认为会有不同的视图属性,我不知道从安全区域开始计算视图框架。还是这一切都取决于约束?
  • 让我发布一个程序化的答案。它可能不完整,甚至毫无价值。但希望两者都不是,并且会有所帮助。 (如果有遗漏,请发表评论,我会进行编辑。)
  • @dfd 那太好了。我至少可以将其用作指南。

标签: swift uibutton ios11 cgrect iphone-x


【解决方案1】:

安全区,特别是safeAreaLayoutGuide,与约束有关。

有多种方法可以编写自动布局约束(Visual Format Layout 或 VFL,显式 - 和冗长 - NSLayoutConstraints),但我更喜欢使用“布局锚点”。

任何布局的基本思想都是positionsize。给某个东西一个宽度/高度和 x/y 轴值,你就知道了。很像框架。

所以基本的“锚”布局方式:

micButton.frame = CGRect.init(x: self.tabBar.center.x - 62, y: self.view.bounds.height - 94, width: 124, height: 124)

会是这样的:

let micButton = UIButton()
micButton.translatesAutoresizingMaskIntoConstraints = false

micButton.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -94).isActive = true
micButton.widthAnchor.constraint(equalToConstant: 124).isActive = true
micButton.heightAnchor.constraint(equalToConstant: 124).isActive = true
micButton.centerXAnchor.constraint(equalTo: view.centerXAnchor, constant: -62).isActive = true

一些注意事项:

  • 是的,您不指定框架。实际上,您只是在可能的情况下进行初始化。
  • 由于您没有使用 IB,因此您需要将自动调整大小掩码标志设置为 false。即使是最有经验的开发人员有时也会忘记这一点。 (这样做通常意味着您会得到“意想不到的”结果,从看不到东西到看到它放错了位置。)
  • 我设置了底部或 Y 轴、宽度和高度,最后是 X 轴。

虽然它比使用框架需要更多代码,但您获得的是跨屏幕尺寸的一致性。

但您想要更多 - 您需要为 iPhone X 安全区域编写代码。你有两个 Apple 给你的工具:layoutMarginsGuidesafeAreaLayoutGuide。前者是在 iOS 9 中引入的(以及更简单的 layoutGuide 和布局锚点),而后者是在 iOS 11 中引入的。

[我的假设可能是一个安全的假设,即所有 iPhone X 设备都将运行 iOS 11 或更高版本。由于“安全区域”实际上只需要此设备,因此您需要以下内容。]

边距适用于 所有 设备,用于前/后(或水平)边缘。它们也适用于顶部/底部(或垂直)边缘。但对于 iPhone X,您需要关注不同的顶部/底部,即“安全区域”。

let layoutGuideTop = UILayoutGuide()
let layoutGuideBottom = UILayoutGuide()
view.addLayoutGuide(layoutGuideTop)
view.addLayoutGuide(layoutGuideBottom)
let margins = view.layoutMarginsGuide
view.addLayoutGuide(margins)
if #available(iOS 11, *) {
    let guide = view.safeAreaLayoutGuide
    layoutGuideTop.topAnchor.constraintEqualToSystemSpacingBelow(guide.topAnchor, multiplier: 1.0).isActive = true
    layoutGuideBottom.bottomAnchor.constraintEqualToSystemSpacingBelow(guide.bottomAnchor, multiplier: 1.0).isActive = true
} else {
    layoutGuideTop.topAnchor.constraint(equalTo: topLayoutGuide.bottomAnchor).isActive = true
    layoutGuideBottom.bottomAnchor.constraint(equalTo: bottomLayoutGuide.topAnchor).isActive = true
}

上面的代码 sn-p 将根据 iOS 版本生成正确的顶部/底部边距变量(layoutGuideToplayoutGuideBottom)。从那里您可以将底部锚点调整为:

micButton.bottomAnchor.constraint(equalTo: laytouGuideBottom, constant: -94).isActive = true

应该将micButton 设置为高于底部安全区域 94 点。

这里有一些链接可以帮助您进行布局锚点和指南:

Layout Anchors

Layout Guides

Safe Area Layout Guides

编辑:

关于约束的最后一点说明。由于您不依赖帧值,所有这些代码最好放在viewDidLoad 中,因为布局引擎会适当地确定所有内容(并且viewDidLayoutSubviews 可能会被多次调用)。

【讨论】:

    【解决方案2】:

    试试这个:

    在 UITabViewController 的类中添加

    micButton.translatesAutoresizingMaskIntoConstraints = false
    view.addSubview(micButton)
      if #available(iOS 11, *) {
           let guide = view.safeAreaLayoutGuide
           micButton.centerXAnchor.constraint(equalTo: guide.centerXAnchor).isActive = true
           micButton.bottomAnchor.constraint(equalTo: guide.bottomAnchor).isActive = true
           micButton.heightAnchor.constraint(equalToConstant: 64).isActive = true
           micButton.widthAnchor.constraint(equalToConstant: 64).isActive = true
    
      } else {
           NSLayoutConstraint(item: micButton, attribute: .centerX, relatedBy: .equal, toItem: view, attribute: .centerX, multiplier: 1.0, constant: 0).isActive = true
           NSLayoutConstraint(item: micButton, attribute: .bottom, relatedBy: .equal, toItem: view, attribute: .bottom, multiplier: 1, constant: 0).isActive = true
           micButton.heightAnchor.constraint(equalToConstant: 64).isActive = true
           micButton.widthAnchor.constraint(equalToConstant: 64).isActive = true
                }
    

    【讨论】:

      【解决方案3】:

      感谢@dfd 的指导,这就是我解决问题的方法。我刚刚检查了使用该应用程序的手机是否具有与 iPhoneX 相同的屏幕尺寸,并且我只是更改了约束。

       override func viewDidLoad() {
          super.viewDidLoad()
      
          //Frame mic button
          micButton.frame = CGRect.init(x: self.tabBar.center.x - 62, y: self.view.bounds.height - 94, width: 124, height: 124)
          micButton.layer.cornerRadius = 62
          micButton.translatesAutoresizingMaskIntoConstraints = false
      
          micButton.setBackgroundImage(#imageLiteral(resourceName: "micIcon"), for: .normal)
          //Add to tabbar view
          self.view.insertSubview(micButton, aboveSubview: self.tabBar)
      
      
          if UIDevice().userInterfaceIdiom == .phone {
              if UIScreen.main.nativeBounds.height == 2436 {
                  //iPhoneX Device
                  micButton.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: 0).isActive = true
                  micButton.widthAnchor.constraint(equalToConstant: 124).isActive = true
                  micButton.heightAnchor.constraint(equalToConstant: 124).isActive = true
                  micButton.centerXAnchor.constraint(equalTo: self.tabBar.centerXAnchor, constant: 0).isActive = true
              } else {
                  //Not an iPhoneX Device
                  micButton.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: 25).isActive = true
                  micButton.widthAnchor.constraint(equalToConstant: 124).isActive = true
                  micButton.heightAnchor.constraint(equalToConstant: 124).isActive = true
                  micButton.centerXAnchor.constraint(equalTo: self.tabBar.centerXAnchor, constant: 0).isActive = true
              }
      
          }
      
          // Do any additional setup after loading the view.
      }
      

      【讨论】:

        【解决方案4】:

        此代码可帮助您解决问题。

        micButton.bottomAnchor.constraint(equalTo:view.safeAreaLayoutGuide.bottomAnchor).isActive = true
        

        【讨论】:

        • 你能解释一下你的代码是做什么的吗?
        • 用来解决这个问题一般我们使用view.bottomAnchor但是这个问题我们要使用safeAreaLayoutGuide。
        猜你喜欢
        • 2019-01-20
        • 1970-01-01
        • 2018-05-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多