簡體   English   中英

iPhone X 如何處理 View Controller inputAccessoryView?

[英]iPhone X how to handle View Controller inputAccessoryView?

我有一個消息應用程序,它具有全屏表格視圖底部文本字段的典型 UI 設計。 我將該文本字段設置為視圖控制器的inputAccessoryView並調用ViewController.becomeFirstResponder()以使該字段顯示在屏幕底部。

我知道這是 Apple 推薦的完成此 UI 結構的方法,它在“經典”設備上完美運行,但是當我在 iPhone X 模擬器上測試時,我注意到使用這種方法時,文本字段不尊重新的“安全區域” . 文本字段呈現在主屏幕指示器下方的屏幕最底部。

我查看了 HIG 文檔,但沒有發現任何關於視圖控制器上的inputAccessoryView有用的信息。

這很困難,因為使用這種方法我實際上並沒有直接控制任何約束,我只是設置inputAccessoryView並讓視圖控制器從那里處理 UI。 所以我不能只將這個領域限制在新的安全區域。

有沒有人在這方面找到好的文檔或知道在 iPhone X 上運行良好的替代方法?

在此處輸入圖片說明

iPhone X 上的inputAccessoryView和安全區域

  • 當鍵盤不可見時, inputAccessoryView固定在屏幕的最底部。 沒有辦法解決這個問題,我認為這是有意的行為。

  • 設置為inputAccessoryView的視圖的layoutMarginsGuide (iOS 9+) 和safeAreaLayoutGuide (iOS 11) 屬性都尊重安全區域,即在 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
    }
}

在此處查看結果

這是 iPhone X 上 inputAccessoryViews 的一個普遍問題。 inputAccessoryView 忽略其窗口的 safeAreaLayoutGuides。

要修復它,當視圖移動到其窗口時,我們必須在您的類中手動添加約束:

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

PS:這里的self指的是inputAccessoryView。

我在這里詳細寫過: http : //ahbou.org/post/165762292157/iphone-x-inputaccessoryview-fix

在 Xib 中,在您的設計底部找到一個正確的約束,並將 item 設置為Safe Area而不是Superview

之前 在此處輸入圖片說明

修復 在此處輸入圖片說明

之后 在此處輸入圖片說明

我剛剛創建了一個名為SafeAreaInputAccessoryViewWrapperView的快速CocoaPod來解決這個問題。 它還使用自動布局約束動態設置包裝視圖的高度,因此您不必手動設置框架。 支持 iOS 9+。

以下是如何使用它:

  1. 使用SafeAreaInputAccessoryViewWrapperView(for:)包裹任何 UIView/UIButton/UILabel/etc :

     SafeAreaInputAccessoryViewWrapperView(for: button)
  2. 在你的類中的某個地方存儲對此的引用:

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

     override var inputAccessoryView: UIView? { return wrappedButton }
  4. (可選)始終顯示inputAccessoryView ,即使鍵盤關閉:

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

祝你好運!

只需為 JSQMessagesInputToolbar 添加一個擴展

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

重復: jsqmessageviewcontroller ios11 工具欄

似乎這是一個 iOS 錯誤,並且存在一個 rdar 問題: inputAccessoryViews 應該尊重 iPhone X 上帶有外部鍵盤的安全區域插入

我想這應該在 iPhone X 出現時在 iOS 更新中修復。

在 iOS 自動引導安全插入之前,簡單的解決方法是將您的配件包裝在容器視圖中,並在配件視圖和容器視圖之間設置底部空間約束以匹配窗口的安全區域插入。

注意:當然,當 iOS 更新修復附件視圖的底部間距時,此解決方法可以使附件視圖間距從底部增加一倍。

例如

- (void) didMoveToWindow {
    [super didMoveToWindow];
    if (@available(iOS 11.0, *)) {
        self.bottomSpaceConstraint.constant = self.window.safeAreaInsets.bottom;
    }
}

來自代碼(Swift 4)。 想法 - 監控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

結果:

在此處輸入圖片說明 在此處輸入圖片說明

我剛剛在 Github上創建了一個支持 iPhone X 的項目。它尊重新的安全區域布局指南。 使用:

autoresizingMask = [.flexibleHeight]

截圖:

截圖

最簡單的答案(只有一行代碼)

只需使用繼承自UIToolbar而不是UIView的自定義視圖作為inputAccessoryView

另外不要忘記將該自定義視圖的自動調整大小掩碼設置為UIViewAutoresizingFlexibleHeight

就是這樣。 以后謝謝我。

經過大量研究和試驗,這似乎是嘗試將UIToolbar與文本輸入的inputAccessoryView一起使用的inputAccessoryView解決方案。 (大多數現有的解決方案是使用固定的附件視圖而不是將其分配給文本視圖(並在鍵盤關閉時隱藏它)。)

該代碼的靈感來自https://stackoverflow.com/a/46510833/2603230 基本上,我們首先創建一個具有工具欄子視圖的自定義視圖:

class CustomInputAccessoryWithToolbarView: UIView {
    public var toolbar: UIToolbar!

    override init(frame: CGRect) {
        super.init(frame: frame)

        // https://stackoverflow.com/a/58524360/2603230
        toolbar = UIToolbar(frame: frame)

        // Below is adopted from https://stackoverflow.com/a/46510833/2603230
        self.addSubview(toolbar)

        self.autoresizingMask = .flexibleHeight

        toolbar.translatesAutoresizingMaskIntoConstraints = false
        toolbar.leadingAnchor.constraint(
            equalTo: self.leadingAnchor,
            constant: 0
        ).isActive = true
        toolbar.trailingAnchor.constraint(
            equalTo: self.trailingAnchor,
            constant: 0
        ).isActive = true
        toolbar.topAnchor.constraint(
            equalTo: self.topAnchor,
            constant: 0
        ).isActive = true
        // This is the important part:
        if #available(iOS 11.0, *) {
            toolbar.bottomAnchor.constraint(
                equalTo: self.safeAreaLayoutGuide.bottomAnchor,
                constant: 0
            ).isActive = true
        } else {
            toolbar.bottomAnchor.constraint(
                equalTo: self.layoutMarginsGuide.bottomAnchor,
                constant: 0
            ).isActive = true
        }
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    // https://stackoverflow.com/a/46510833/2603230
    // 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
    }
}

然后您可以將其設置為通常用於文本輸入的inputAccessoryView :(您應該指定框架大小以避免在UIToolbar 中看到的警告與 UIBarButtonItem LayoutConstraint 問題

let myAccessoryView = CustomInputAccessoryWithToolbarView(frame: CGRect(x: 0, y: 0, width: self.view.frame.size.width, height: 44))
textView.inputAccessoryView = myAccessoryView

當你想與工具欄進行交互時(例如,在工具欄上設置項目),你可以簡單地引用toolbar變量:

myAccessoryView.toolbar.setItems(myToolbarItems, animated: true)

演示:(在模擬器中使用硬件鍵盤/Command+K)

之前: 工具欄擋住了主頁指示器

之后: 工具欄不會阻礙主頁指示器

如果您已經通過 nib 文件加載了自定義視圖。

添加一個像這樣的便利構造函數:

convenience init() {
    self.init(frame: .zero)
    autoresizingMask = .flexibleHeight
}

並覆蓋intrinsicContentSize

override var intrinsicContentSize: CGSize {
    return .zero
}

nib將第一個底部約束(應保持在安全區域上方的視圖)設置safeArea ,將第二個superviewpriority較低的safeArea superview ,以便在較舊的 iOS 上可以滿足。

在沒有變通方法的情況下對我有用的解決方案:

我使用UIInputViewController通過重寫提供輸入附件視圖inputAccessoryViewController屬性,而不是inputAccessoryView在“主”視圖控制器。

UIInputViewControllerinputView設置為我的自定義輸入視圖( UIInputView子類)。

實際上對我allowsSelfSizing是將我的UIInputView allowsSelfSizing屬性設置為true 輸入視圖內的約束使用安全區域,並以定義總視圖高度的方式設置(類似於自動調整表格視圖單元格的大小)。

-- 對於那些使用 JSQMessagesViewController 庫的人 --

我提出了一個基於 JSQ 最新develop分支提交的固定分支。

它使用的是didMoveToWindow解決方案(我相信來自 @jki?)。 不理想但值得嘗試,同時等待 Apple 關於inputAccessoryView的安全區域布局指南附件或任何其他更好的修復的答案。

您可以將其添加到 Podfile 中,替換之前的 JSQ 行:

pod 'JSQMessagesViewController', :git => 'https://github.com/Tulleb/JSQMessagesViewController.git', :branch => 'develop', :inhibit_warnings => true

我只是將安全區域添加到 inputAccessoryView (Xcode 中的復選框)。 並更改底部空間約束等於安全區域的底部而不是 inputAccessoryView 根視圖底部。

約束

結果

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM