Skip to content

Instantly share code, notes, and snippets.

@agiletortoise
Created October 5, 2017 15:20
Show Gist options
  • Star 11 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save agiletortoise/82e5e378e3483ab835db49aefed300e3 to your computer and use it in GitHub Desktop.
Save agiletortoise/82e5e378e3483ab835db49aefed300e3 to your computer and use it in GitHub Desktop.
How to update input accessory view to handle iPhone X with external keyboard
//
// ViewController.swift
// TEST_InputViewX
//
// Created by Greg Pierce on 10/5/17.
// Copyright © 2017 Agile Tortoise. All rights reserved.
//
// Demonstrates how to update an input accessory view
// to work on iPhone X with external keyboard attached
// using safe area insets.
//
// This adds the necessary padding under the input accessory
// So it's not clipped by home button area.
import UIKit
class ViewController: UIViewController, UITextViewDelegate {
struct Constants {
static let AccessoryViewHeight: CGFloat = 44.0
}
@IBOutlet weak var textView: UITextView!
var accessoryView: UIView?
override func viewDidLoad() {
super.viewDidLoad()
// observe keyboard events
let nc = NotificationCenter.default
nc.addObserver(self, selector: #selector(keyboardDidShow(_:)), name: NSNotification.Name.UIKeyboardDidShow, object: nil)
nc.addObserver(self, selector: #selector(keyboardWillHide(_:)), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
nc.addObserver(self, selector: #selector(keyboardWillChangeFrame(_:)), name: NSNotification.Name.UIKeyboardWillChangeFrame, object: nil)
}
func textViewShouldBeginEditing(_ textView: UITextView) -> Bool {
buildInputView()
textView.inputAccessoryView = accessoryView
return true
}
func buildInputView() {
// create input accessory view
guard accessoryView == nil else { return }
let accView = UIView(frame: CGRect(x: 0.0, y: 0.0, width: self.view.bounds.size.width, height: Constants.AccessoryViewHeight))
accView.autoresizingMask = [.flexibleHeight] // important! allows it to resize
accView.backgroundColor = .red
// create a nested content view for your actual input controls
let contentView = UIView(frame: .zero)
contentView.backgroundColor = .green
// constrain content view to safe area of parent view
contentView.translatesAutoresizingMaskIntoConstraints = false
accView.addSubview(contentView)
contentView.leadingAnchor.constraint(equalTo: accView.safeAreaLayoutGuide.leadingAnchor).isActive = true
contentView.trailingAnchor.constraint(equalTo: accView.safeAreaLayoutGuide.trailingAnchor).isActive = true
contentView.topAnchor.constraint(equalTo: accView.safeAreaLayoutGuide.topAnchor).isActive = true
contentView.heightAnchor.constraint(equalToConstant: Constants.AccessoryViewHeight).isActive = true
accessoryView = accView
}
func updateInputViewFrame() {
guard let accView = accessoryView else { return }
// calculate the accessory view height based on safe insets
let newHeight = accView.safeAreaInsets.bottom + Constants.AccessoryViewHeight
if newHeight != accView.frame.size.height {
accView.frame = CGRect(x: 0.0, y: 0.0, width: self.view.bounds.size.width, height: newHeight)
textView.reloadInputViews()
}
}
@objc func keyboardDidShow(_ note: NSNotification) {
// update height of accessory view if needed
updateInputViewFrame()
}
@objc func keyboardWillChangeFrame(_ note: NSNotification) {
// update height of accessory view if needed
updateInputViewFrame()
}
@objc func keyboardWillHide(_ note: NSNotification) {
}
@IBAction func hideKeyboard(_ sender: Any) {
textView.resignFirstResponder()
}
}
@aheze
Copy link

aheze commented Feb 12, 2022

Thanks! The if newHeight != accView.frame.size.height { seems to be pretty important, otherwise the device will get stuck in an endless cycle of keyboardDidShow getting called.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment