Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/iphone/42.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Ios iPhone X如何处理视图控制器inputAccessoryView?_Ios_Iphone_Inputaccessoryview_Iphone X - Fatal编程技术网

Ios iPhone X如何处理视图控制器inputAccessoryView?

Ios iPhone X如何处理视图控制器inputAccessoryView?,ios,iphone,inputaccessoryview,iphone-x,Ios,Iphone,Inputaccessoryview,Iphone X,我有一个消息传递应用程序,在全屏表格视图的底部有一个文本字段的典型UI设计。我正在将该文本字段设置为视图控制器的inputAccessoryView,并调用ViewController.becomeFirstResponder(),以使该字段显示在屏幕底部 我知道这是苹果公司推荐的实现这种UI结构的方法,它在“经典”设备上运行得非常完美。但是,当我在iPhoneX模拟器上进行测试时,我注意到使用这种方法时,文本字段不尊重新的“安全区域”。文本字段呈现在主屏幕指示器下方的屏幕底部 我查看了HIG文

我有一个消息传递应用程序,在全屏表格视图的底部有一个文本字段的典型UI设计。我正在将该文本字段设置为视图控制器的
inputAccessoryView
,并调用
ViewController.becomeFirstResponder()
,以使该字段显示在屏幕底部

我知道这是苹果公司推荐的实现这种UI结构的方法,它在“经典”设备上运行得非常完美。但是,当我在iPhoneX模拟器上进行测试时,我注意到使用这种方法时,文本字段不尊重新的“安全区域”。文本字段呈现在主屏幕指示器下方的屏幕底部

我查看了HIG文档,但没有发现任何关于视图控制器上的
inputAccessoryView
的有用信息

这很困难,因为使用这种方法,我实际上并没有直接控制任何约束,我只是设置
inputAccessoryView
,让视图控制器从那里处理UI。所以我不能把战场限制在新的安全区域

有没有人找到这方面的好文档,或者知道另一种在iPhoneX上运行良好的方法


我只是想将安全区域添加到inputAccessoryView(Xcode中的复选框)。并将底部空间约束更改为安全区域的底部,而不是inputAccessoryView根视图底部


这似乎是一个iOS错误,它有一个rdar问题:


我想这应该在iPhone X出现时在iOS更新中修复。

这是iPhone X上InputAccessoryView的一个普遍问题。 inputAccessoryView忽略其窗口的安全区域布局指南

要解决此问题,我们必须在视图移动到其窗口时在类中手动添加约束:

override func didMoveToWindow() {
    super.didMoveToWindow()
    if #available(iOS 11.0, *) {
        if let window = self.window {
            self.bottomAnchor.constraintLessThanOrEqualToSystemSpacingBelow(window.safeAreaLayoutGuide.bottomAnchor, multiplier: 1.0).isActive = true
        }
    }
}
注:self在这里指的是inputAccessoryView


我在这里详细描述了这一点:

在iOS自动引导安全区域插入之前,简单的解决方法是将附件包装在容器视图中,并在accesory视图和容器视图之间设置底部空间约束,以匹配窗口的安全区域插入

注意:当然,当iOS update修复附件视图的底部间距时,此解决方法可以使附件视图的底部间距加倍

例如

iPhone X上的inputAccessoryView和安全区域
  • 当键盘不可见时,
    inputAccessoryView
    固定在屏幕的最底部。这是没有办法的,我认为这是故意的行为

  • 视图的
    layoutMarginsGuide
    (iOS 9+)和
    safeAreaLayoutGuide
    (iOS 11)属性设置为
    inputAccessoryView
    ,这两个属性都尊重安全区域,即在iPhone X上:

    • 当键盘不可见时,
      bottomAnchor
      将显示主页按钮区域
    • 显示键盘时,
      bottomAnchor
      位于
      inputAccessoryView
      的底部,这样它就不会在键盘上方留下无用的空间
工作示例:

import UIKit

class ViewController: UIViewController {

    override var canBecomeFirstResponder: Bool { return true }

    var _inputAccessoryView: UIView!

    override var inputAccessoryView: UIView? {

        if _inputAccessoryView == nil {

            _inputAccessoryView = CustomView()
            _inputAccessoryView.backgroundColor = UIColor.groupTableViewBackground

            let textField = UITextField()
            textField.borderStyle = .roundedRect

            _inputAccessoryView.addSubview(textField)

            _inputAccessoryView.autoresizingMask = .flexibleHeight

            textField.translatesAutoresizingMaskIntoConstraints = false

            textField.leadingAnchor.constraint(
                equalTo: _inputAccessoryView.leadingAnchor,
                constant: 8
            ).isActive = true

            textField.trailingAnchor.constraint(
                equalTo: _inputAccessoryView.trailingAnchor,
                constant: -8
            ).isActive = true

            textField.topAnchor.constraint(
                equalTo: _inputAccessoryView.topAnchor,
                constant: 8
            ).isActive = true

            // this is the important part :

            textField.bottomAnchor.constraint(
                equalTo: _inputAccessoryView.layoutMarginsGuide.bottomAnchor,
                constant: -8
            ).isActive = true
        }

        return _inputAccessoryView
    }

    override func loadView() {

        let tableView = UITableView()
        tableView.keyboardDismissMode = .interactive

        view = tableView
    }
}

class CustomView: UIView {

    // this is needed so that the inputAccesoryView is properly sized from the auto layout constraints
    // actual value is not important

    override var intrinsicContentSize: CGSize {
        return CGSize.zero
    }
}

我刚刚创建了一个名为CocoaPod的快速CocoaPod来解决这个问题。它还使用自动布局约束动态设置包裹视图的高度,因此您不必手动设置框架。支持iOS 9+

以下是如何使用它:

  • 使用
    安全区域InputAccessoryViewRapperView(for:)
    包装任何UIView/UIButton/UILabel/etc:

  • 在类中的某个位置存储对此的引用:

    let button = UIButton(type: .system)
    
    lazy var wrappedButton: SafeAreaInputAccessoryViewWrapperView = {
        return SafeAreaInputAccessoryViewWrapperView(for: button)
    }()
    
  • 返回
    inputAccessoryView
    中的引用:

    override var inputAccessoryView: UIView? {
        return wrappedButton
    }
    
  • (可选)即使键盘关闭,也始终显示
    inputAccessoryView

    override var canBecomeFirstResponder: Bool {
        return true
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        becomeFirstResponder()
    }
    

  • 祝你好运

    在Xib中,在设计的底部找到一个正确的约束,并将项目设置为
    安全区域
    ,而不是
    Superview

    override var inputAccessoryView: UIView? {
        return wrappedButton
    }
    
    之前

    修复

    之后

    --适用于使用JSQMessagesViewController库的用户--

    我建议基于JSQ最新的
    develope
    branch提交的固定fork

    它使用的是
    didMoveToWindow
    解决方案(我相信来自@jki?)。这并不理想,但在等待苹果公司关于
    inputAccessoryView
    的安全区域布局指南附件或其他更好的解决方案的答案时值得一试

    您可以将其添加到Podfile中,以替换以前的JSQ行:

    pod 'JSQMessagesViewController', :git => 'https://github.com/Tulleb/JSQMessagesViewController.git', :branch => 'develop', :inhibit_warnings => true
    
    来自代码(Swift 4)。Idea-监视
    layoutMarginsDidChange
    事件并调整
    intrinsicContentSize

    public final class AutoSuggestionView: UIView {
    
       private lazy var tableView = UITableView(frame: CGRect(), style: .plain)
       private var bottomConstraint: NSLayoutConstraint?
       var streetSuggestions = [String]() {
          didSet {
             if streetSuggestions != oldValue {
                updateUI()
             }
          }
       }
       var handleSelected: ((String) -> Void)?
    
       public override func initializeView() {
          addSubview(tableView)
          setupUI()
          setupLayout()
          // ...
          updateUI()
       }
    
       public override var intrinsicContentSize: CGSize {
          let size = super.intrinsicContentSize
          let numRowsToShow = 3
          let suggestionsHeight = tableView.rowHeight * CGFloat(min(numRowsToShow, tableView.numberOfRows(inSection: 0)))
          //! Explicitly used constraint instead of layoutMargins
          return CGSize(width: size.width,
                        height: suggestionsHeight + (bottomConstraint?.constant ?? 0))
       }
    
       public override func layoutMarginsDidChange() {
          super.layoutMarginsDidChange()
          bottomConstraint?.constant = layoutMargins.bottom
          invalidateIntrinsicContentSize()
       }
    }
    
    extension AutoSuggestionView {
    
       private func updateUI() {
          backgroundColor = streetSuggestions.isEmpty ? .clear : .white
          invalidateIntrinsicContentSize()
          tableView.reloadData()
       }
    
       private func setupLayout() {
    
          let constraint0 = trailingAnchor.constraint(equalTo: tableView.trailingAnchor)
          let constraint1 = tableView.leadingAnchor.constraint(equalTo: leadingAnchor)
          let constraint2 = tableView.topAnchor.constraint(equalTo: topAnchor)
          //! Used bottomAnchor instead of layoutMarginGuide.bottomAnchor
          let constraint3 = bottomAnchor.constraint(equalTo: tableView.bottomAnchor)
          bottomConstraint = constraint3
          NSLayoutConstraint.activate([constraint0, constraint1, constraint2, constraint3])
       }
    }
    
    用法:

    let autoSuggestionView = AutoSuggestionView()
    // ...
    textField.inputAccessoryView = autoSuggestionView
    
    结果:


    如果您已经通过nib文件加载了自定义视图

    添加这样一个方便的构造函数:

    convenience init() {
        self.init(frame: .zero)
        autoresizingMask = .flexibleHeight
    }
    
    override var intrinsicContentSize: CGSize {
        return .zero
    }
    
    和重写
    intrinsicContentSize

    convenience init() {
        self.init(frame: .zero)
        autoresizingMask = .flexibleHeight
    }
    
    override var intrinsicContentSize: CGSize {
        return .zero
    }
    
    nib
    中,将第一个底部约束(应位于安全区域上方的视图)设置为
    safeArea
    ,将第二个底部约束设置为
    superview
    ,优先级较低
    ,以便在较旧的iOS上可以满足


    只需为JSQMessageInputToolbar添加一个扩展

    extension JSQMessagesInputToolbar {
        override open func didMoveToWindow() {
            super.didMoveToWindow()
            if #available(iOS 11.0, *) {
                if self.window?.safeAreaLayoutGuide != nil {
                self.bottomAnchor.constraintLessThanOrEqualToSystemSpacingBelow((self.window?.safeAreaLayoutGuide.bottomAnchor)!,
                                                                                multiplier: 1.0).isActive = true
                }
            }
         }
    }
    
    重复:

    我刚刚创建了一个支持iPhone X的应用程序。它尊重新的安全区域布局指南。使用:

    autoresizingMask = [.flexibleHeight]
    
    截图:

    最简单的答案(只需一行代码) 只需使用从
    UIToolbar
    继承的自定义视图,而不是
    UIView
    作为
    inputAccessoryView

    另外,不要忘记将自定义视图的自动调整大小掩码设置为
    uiviewsautoresizingflexibleheight


    就这样。稍后感谢我。

    在没有解决办法的情况下对我有效的解决方案:

    我正在使用
    UIInputViewController
    通过覆盖
    inputAccessoryViewController
    属性而不是
    inputAccessoryV来提供输入附件视图