Strange delay displaying text in input accessory view during push animation.
I have come up with the solution which works on both iOS 8 and 9. Also it address retain cycle issue presented in iOS 9 which prevent view controller from being deallocated when use inputaccessoryview. Check github project for more details.
With lots of experimentation I have found quite hacky solution but works like a charm. Just subclass your implemantation accessory view from AccessoryView listed below.
class AccessoryView: UITextField {
override var canBecomeFirstResponder: Bool {
return true
}
override func awakeFromNib() {
super.awakeFromNib()
disableShowingKeyboard()
hideCursor()
}
}
extension AccessoryView {
private func disableShowingKeyboard() {
inputView = UIView()
}
private func hideCursor() {
tintColor = UIColor.clear
}
override func accessibilityActivate() -> Bool {
return false
}
override var isEditing: Bool {
return false
}
override func caretRect(for position: UITextPosition) -> CGRect {
return .zero
}
override func selectionRects(for range: UITextRange) -> [UITextSelectionRect] {
return []
}
override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
if action == #selector(UIResponder.copy(_:)) || action == #selector(UIResponder.selectAll(_:)) || action == #selector(UIResponder.paste(_:)){
return false
}
return super.canPerformAction(action, withSender: sender)
}
override func addGestureRecognizer(_ gestureRecognizer: UIGestureRecognizer) {
if gestureRecognizer is UILongPressGestureRecognizer {
gestureRecognizer.isEnabled = false
}
super.addGestureRecognizer(gestureRecognizer)
}
}
extension AccessoryView {
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
for view in subviews {
let _point = self.convert(point, to: view)
if !view.isHidden && view.isUserInteractionEnabled && view.alpha > 0.01 && view.point(inside: _point, with: event) {
if let _view = view.hitTest(_point, with: event){
return _view
}
}
}
return super.hitTest(point, with: event)
}
}
UITextView's not displaying content during push animation when is in inputAccessoryView
So I will stick with the solution where the inputAccessoryView
is stored until next appearance.
UITextView in InputAccessoryView not becoming First Responder (Keyboard does not show up initially)
If you want the text view to become active, and the keyboard to show, as soon as the view appears, use:
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
textView.becomeFirstResponder()
}
If you want the text view to be visible at the bottom, and become active / show the keyboard when the textview is tapped, take a look at this answer:
https://stackoverflow.com/a/61508928/6257435
Edit
If you want to push a view controller onto the navigation stack, and have the keyboard showing with a custom input accessory view, containing a text view, and give it the focus...
Add a hidden text field to the controller. In viewDidLoad
tell that text field to use the custom input accessory view and tell it to become first responder.
Then, in viewDidAppear
tell the text view in the custom input accessory view to become the first responder:
class TestViewController: UIViewController {
var hiddenTF = UITextField()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
// set the text field to hidden
hiddenTF.isHidden = true
// add it to the view
view.addSubview(hiddenTF)
// tell hidden text field to use custom input accessory view
hiddenTF.inputAccessoryView = keyboardAccessory
// tell it to become first responder
hiddenTF.becomeFirstResponder()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
// tell the textView (in the custom input accessory view)
// to become first responder
textView.becomeFirstResponder()
}
var textView: UITextView = {
let view = UITextView()
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = .yellow
return view
}()
lazy var keyboardAccessory: UIToolbar = {
let inputAccessory = UIToolbar(frame: .init(x: 0, y: 0, width: 0, height: 100))
inputAccessory.addSubview(textView)
NSLayoutConstraint.activate([
textView.centerXAnchor.constraint(equalTo: inputAccessory.centerXAnchor),
textView.centerYAnchor.constraint(equalTo: inputAccessory.centerYAnchor),
textView.widthAnchor.constraint(equalToConstant: 200),
textView.heightAnchor.constraint(equalToConstant: 50)
])
inputAccessory.backgroundColor = .gray
return inputAccessory
}()
}
UIButton title not set until view appear
Just add...
inputAccessoryView?.snapshotView(afterScreenUpdates: true)
in your viewDidLoad
of the ViewController.
Btw this issue appeared also to others. I found the solution here: https://stackoverflow.com/a/36548797/8932493
Make UITextView parent be its own inputAccessoryView
A much easier solution is to make your input field the input accessory view of your view controller:
- (BOOL)canBecomeFirstResponder
{
return YES;
}
- (UIView *)inputAccessoryView
{
return self.yourInputField;
}
The view will be on screen at the bottom of the screen and when it becomes first responder in response to a user tapping it, the keyboard will be presented. The view will be animated such that it remains immediately above the keyboard.
Hiding/Showing Input Accessory View
I don't know if this is really what you're going for, but give it a try.
Tapping anywhere in the view will show/hide the input accessory view:
class TestInputViewController: UIViewController {
//in viewcontroller
lazy var inputContainerView: CustomInputAccessoryView = {
let frame = CGRect(x: 0, y: 0, width: self.view.frame.width, height: 60)
let customInputAccessoryView = CustomInputAccessoryView(frame: frame)
return customInputAccessoryView
}()
override var canBecomeFirstResponder: Bool {
return true
}
//onLoad
override var inputAccessoryView: UIView? {
get { return inputContainerView }
}
override func viewDidLoad() {
super.viewDidLoad()
// so we can see the frame
inputContainerView.backgroundColor = .blue
// tap anywhere in view to show / hide input accessory view
let g = UITapGestureRecognizer(target: self, action: #selector(didTap(sender:)))
view.addGestureRecognizer(g)
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
}
@objc func didTap(sender: UITapGestureRecognizer) {
if self.isFirstResponder {
resignFirstResponder()
} else {
becomeFirstResponder()
}
}
}
class CustomInputAccessoryView: UIView {
let customTextView: CustomInputTextView = {
let tv = CustomInputTextView()
tv.isScrollEnabled = false
return tv
}()
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = UIColor.white
autoresizingMask = .flexibleHeight
addSubview(customTextView)
customTextView.translatesAutoresizingMaskIntoConstraints = false
customTextView.topAnchor.constraint(equalTo: topAnchor, constant: 12).isActive = true
customTextView.bottomAnchor.constraint(equalTo: safeAreaLayoutGuide.bottomAnchor, constant: -8).isActive = true
customTextView.leftAnchor.constraint(equalTo: leftAnchor, constant: 10).isActive = true
customTextView.rightAnchor.constraint(equalTo: rightAnchor, constant: -10).isActive = true
customTextView.heightAnchor.constraint(greaterThanOrEqualToConstant: frame.height - 20).isActive = true
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override var intrinsicContentSize: CGSize {
return .zero
}
}
class CustomInputTextView: UITextView {
override init(frame: CGRect, textContainer: NSTextContainer?) {
super.init(frame: frame, textContainer: textContainer)
}
override var canResignFirstResponder: Bool {
return true
}
override var canBecomeFirstResponder: Bool {
return true
}
required init?(coder aDecoder: NSCoder) {
fatalError("init coder has not been implemented")
}
}
Not related to showing / hiding, but a few of your constraints were wrong, causing the text view to be misplaced.
AccessoryView on TextView without Keyboard?
inputAccessoryView is for a view to come up above the standard apple virtual keyboard. If you don't allow the on-screen keyboard to appear, then the inputAccessoryView will not appear either.
If you ONLY want a custom keyboard to show, and NOT the standard on-screen keyboard, you should use inputView instead of inputAccessoryView. See the difference between inputView and inputAccessoryView here:
http://developer.apple.com/iphone/library/documentation/uikit/reference/UITextView_Class/Reference/UITextView.html
Related Topics
Swift - Update/Refresh Label That Displays Time
iOS Sound Not Playing in Swift
Nsurlsession/Nsurlconnection Http Load Failed (Kcfstreamerrordomainssl, -9802) on a Subdomain
Problems with Cropping a Uiimage in Swift
Sirikit, How to Display Response for Start Workout Intent
Ios-Charts How to Put Uiimage Beside a Point
Xcode 11 Archive Gives Phasescriptexecution Failed
Game Exits from Pause State After Resuming It from Background in Swift
Firebase Checking for Null Value (Swift)
Gmsmapview Animatetocameraposition Zoom in - Zoom Out Animation
iOS Swift Flood Fill Algorithm
Firebase: Provided Bucket Does Not Match the Storage Bucket of the Current Instance in Swift
Get Light or Dark Variant of a Color Declared in Assets
Cannot Invoke 'Decode' with an Argument List of Type '(T, From: Data)'
iOS Firebase Database Get Key of Value