Keyboard prevents data entry - ios

I have many textFields to fill, i use below code to move the VC modally
#objc func addNewRestaurant() {
let pushController = RestaurantAddController()
pushController.modalPresentationStyle = .fullScreen //or .overFullScreen for transparency
self.present(pushController, animated: true, completion: nil)
}
And below code to move to next line when i press enter
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
if let nextField = view.viewWithTag(textField.tag + 1) as? UITextField {
textField.resignFirstResponder()
nextField.becomeFirstResponder()
}
But my screen always remains covered with keyboard like below and i do not see what beneath, even if i cancel it when i reactivate the keyboard , once again fields are hidden
UPDATE SOLUTION -
I ended up using IQKeyboardManager, its giving 3 warnings but not able to understand how it was done, not a solution from apple but need to move on with project, this is what i am getting now b, see below, thanks

Put your all the fields under scroll view and use "TPKeyboardAvoidingScrollView" class for better experience

Here is an answer. Here is an answer Https://stackoverflow.com/q/26070242/13975969

Related

How can I reach UITextfield in subviews in Swift?

Here is the objects hierarchy in my ViewController in Main.Storyboard
Button same size with superview. I would like to close the keyboard with the button click action that's why I wrote this code scope:
#IBAction func btnCloseKeyboardClick(_ sender: UIButton) {
print("A")
for viewItem in self.view.subviews
{
print("B")
if (viewItem is UITextField)
{
print("C")
let tField = viewItem as! UITextField
tField.resignFirstResponder()
}
}
}
but it does not close the keyboard means I don't see print("C")
I solved this problem like in this link but I would like to ask above situation also
If you only have one textfield in your view and want to dismiss keyboard for that, the simple way is to call view.endEditing(true) in your button action

Weird bug when presenting a view controller

I have a basic app with a UITabBarController as the root view controller. When a user of the app is not signed in I'm showing a modal controller via window!.rootViewController!.present(viewController, animated: true) in my AppDelegate. This works fine on all iPhone models, however the following happens on any iPad:
The background color of the SignInController is visible during the transition. Now comes the weird thing: When I change the view in Interface Builder to an iPad the bug is gone like so:
Changing the background color back to the transparent default removes at least the white background, however the view is still animating from the left bottom which is something I don't want. And by the way, changing the view in Interface Builder breaks the animation on all iPhones. Changing it back fixes it but breaks again all iPads.
This is the code (using ReSwift for state management):
func newState(state: State) {
switch (previousState.session, state.session) {
case (.loading, .notSignedIn), (.signedIn, .loading):
(window!.rootViewController! as! UITabBarController).selectedIndex = 0
let viewController = storyboard.instantiateViewController(withIdentifier: "SignInViewController")
window!.rootViewController!.present(viewController, animated: true, completion: nil)
default:
// more stuff
break
}
}
EDIT: Added the actual code.
I fixed it! 😊
The problem was a combination of having an observer on keyboardWillShowNotification and a becomeFirstResponder in the viewWillAppear method of the presented controller.
Moving the becomeFirstResponder into viewDidAppear fixed all the problems!
Thanks man! Saved my day.. I'm presenting the keyboard from within a tableview cell - I fixed it like this:
private var canPresentKeyboard: Bool = false
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
canPresentKeyboard = true
if _currentlySelectedIdType != .image {
reload(section: .idType)
}
}
func configure(cell: NumberIdTableViewCellInput) {
cell.set(delegate: self)
if canPresentKeyboard {
cell.clearAndSetFirstResponder()
}
}
I know the code is a bit out of context, but I believe the intention is clear.

Messenger style UITextView input

I'm working on a simple chat as a part of my App.
I have a list of Chats (i.e. friends) in a Table View Controller and Chat View Controller displaying messages for each chat. Both of them are embedded in a NavigationController, which is embedded in a TabBarController.
I want to place UITextView message text input field in a ChatViewController below my UITableVeiw that shows messages of that chat. I would also like the UITextView to look like it's embedded in a tab bar.
I've run through dozens of manuals tutorials and guides and that's where I got so far.
App simulator screenshots here: nice at the top, buggy at the bottom
Main.storyboard screenshot here
I use UITextView instead of UITextField because I want it to be able to change its size depending on content size
I use UIViewController instead of UITableViewController to be able to add another UIView along with UITableView
I don't use actual UITabBar to embed my UITextView because it behaves really weird when UITextView needs to change its height. And as far as I understood Apple doesn't support embedding UI element to UITabBar
What I do is I hide TabBar in ChatViewController viewWillAppear:
override func viewWillAppear(_ animated: Bool) {
tabBarController?.tabBar.isHidden = true
}
And I show it again in parent ChatsTableViewController:
override func viewWillAppear(_ animated: Bool) {
tabBarController?.tabBar.isHidden = false
}
Once TabBar is hidden, my custom stackView embedding textView and sendButton takes its place and operates quite normally as I want it to.
To handle keyboard behaviour properly I have an outlet for my inputStackView bottom constraint (selected on screenshot), which I update on keyboardWillShow/Hide Notifications. Code for that looks like this:
#IBOutlet weak var inputBottomConstraint: NSLayoutConstraint!
var inputBottomConstraintInitialValue: CGFloat!
//MARK: - Keyboard handling
private func enableKeyboardHideOnTap(){
NotificationCenter.default.addObserver(self, selector: #selector(ChatTableViewController.keyboardWillShow(notification:)), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(ChatTableViewController.keyboardWillHide(notification:)), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(ChatTableViewController.hideKeyboard))
self.view.addGestureRecognizer(tap)
}
#objc func hideKeyboard() {
textView.resignFirstResponder()
}
#objc func keyboardWillShow(notification: NSNotification) {
let info = notification.userInfo!
let keyboardFrame: CGRect = (info[UIKeyboardFrameEndUserInfoKey] as! NSValue).cgRectValue
let duration = info[UIKeyboardAnimationDurationUserInfoKey] as! Double
UIView.animate(withDuration: duration) { [weak self] () -> Void in
self?.inputBottomConstraint.constant = keyboardFrame.size.height + 8
self?.view.layoutIfNeeded()
}
}
#objc func keyboardWillHide(notification: NSNotification) {
let duration = notification.userInfo![UIKeyboardAnimationDurationUserInfoKey] as! Double
UIView.animate(withDuration: duration) { [weak self] () -> Void in
guard let `self` = self else { return }
self.inputBottomConstraint.constant = self.inputBottomConstraintInitialValue
self.view.layoutIfNeeded()
}
}
Everything seems to work fine at this point:
TabBar is hidden in a ChatViewController and is shown in ChatsTableViewController
textView and sendButton embedded in a stackView slide along will keyboard up and down, as shown in simulator screenshots above.
The BUG appears when I swipe between ChatViewController and ChatsTableViewController instead of using NavigationControllers
When I start swiping back to ChatsTableViewController, it perform its viewWillAppear method, so tabBarController's tabBar become visible. And if I don't finish that swipe to ChatsTableViewController and go back ChatViewController, ChatViewController's viewWillAppear sets it back to invisible, but the input stackView doesn't get back to it's initial position. It just hangs there above the invisible tabBar.
I've tried moving
tabBarController?.tabBar.isHidden = false
from viewWillAppear to viewDidAppear in ChatsTableViewController.
It fixes the "floating input" issue but it adds a lag of about a second, when ChatsTableViewController is showing without tabBar. Looks not too good either.
Any ideas how that could be fixed properly?
I've been struggling with that issue for about a week))
Because your ChatsTableViewController is embedded in in UINavigationController and your ChatViewController is pushed you can override this hidesBottomBarWhenPushed in ChatViewController which will determine to show your view controller or not:
public override var hidesBottomBarWhenPushed: Bool {
get { return true }
set { super.hidesBottomBarWhenPushed = newValue }
}
This is a better way to hide the tab bar to my mind, because the frame of the view controllers will be adjusted.
Why do you change constraint on keyboard show/Hide. Try to Google inputAccesoryView, many chats use it. It’s natural behaviour is to go with the keyboard. Try FaceBook Messenger for example and see there how swiping hides the keyboard and Textfield together, you can’t accomplish this kind od behaviour with regular view.

Unwanted Keyboard after Back Navigation

All UIViewControllers in my app are managed by a top level UINavigationController. In the UIViewController that is currently on top of my navigation stack I have a set of UITextFields.
A problem occurs when I call becomeFirstResponder() on one of these text fields and then immediately navigate back without changing focus first, e.g. by tapping on another field. After navigating back one level, the keyboard appears, and I have found no way to keep it from appearing or making it disappear. It even stays as I further push views of the navigation stack.
This is directly or indirectly connected to the
becomeFirstResponder() call, because without that call, the
problem does not occur.
Even if I, for test purposes, call
resignFirstResponder() immediately after becomeFirstResponder()
the keyboard still appears after navigating back.
I have tried other ways like detecting and resigning the first responder or calling endEditing() in viewWillDisappear() but did not succeed. I’m not even sure what this keyboard belongs to after the corresponding view is popped off the stack. I cannot inspect the keyboard in the View Debugger as it does not appear there.
Why does the keyboard appear, and how can I prevent it?
It turns out that the form validation that was grabbing the first responder kept reclaiming it until the field content was valid. If it does not release the status before back navigation the keyboard stays and it becomes difficult to assign first responder to another control.
Solution in my case was to more carefully keep track of which field is first responder, detect the back button push, allow resigning first responder unconditionally in that case, and then resign first responder for that field.
var currentTextField: UITextField?
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(animated)
if let currentField = self.currentTextField {
currentField.resignFirstResponder()
}
}
override public func willMoveToParentViewController(parent: UIViewController?) {
if (parent == nil) {
backButtonPushed = true
}
super.willMoveToParentViewController(parent)
}
func customTextFieldDidBeginEditing(textField: UITextField) {
currentTextField = textField
}
public func textFieldShouldEndEditing(textField: UITextField) -> Bool {
// ...
// Must return true if back button is pushed.
if backButtonPushed {
return true
} else {
// ...
}
}
Did you try to call endEditing() in viewWillAppear() of the new VC instead?

How to hide Bar Button Items when UITableView is in edit mode? (Swift)

Title essentially says it all.
I have a UITableView and I want the RightBarButtonItem item to disappear while the UITableView is in edit mode. Unfortunately, all of the answers that I have found so far suggest setting the button to nil... which won't work for me because I do not want to get rid of the button and the reference to it, just hide it while the UITableView is in edit mode.
What I'm having trouble figuring out what to do, then, is:
Detect when the UITableView has entered editing mode
Hiding the RightBarButtonItem (not removing it entirely)
Detect when the UITableView has left editing mode (so the button can reappear)
Any help would be appreciated, thank you!
My working solution for anyone who needs it:
override func setEditing(editing: Bool, animated: Bool) {
if (editing) {
super.setEditing(true, animated: true)
self.navigationItem.rightBarButtonItem!.enabled = false
} else {
super.setEditing(false, animated: true)
self.navigationItem.rightBarButtonItem!.enabled = true
}
}
Make sure to set the super.setEditing before and after editing starts in order to preserve the functionality of the edit button.
In addition, if you don't want the UITableView to remain in edit mode when the user leaves the UITableView and doesn't click "done" first, add the following function:
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(true)
if (editing) {
editing = false
}
}
You can use an optional datasource method to detect when a row is being edited, tableView(_:canEditRowAtIndexPath:)
And inside that method, you can either hide or disable the bar button item (disabling is probably the friendlier thing to do, in terms of the UI and code). There is no hidden property on a bar button, so to properly hide it means you potentially do some grody coding to temporarily remove or make it disappear.
Anyways, I suggest something like:
func tableView(tableView: UITableView!, canEditRowAtIndexPath indexPath: NSIndexPath!) -> Bool {
self.navigationItem.rightBarButtonItem.enabled = false
return true
}
I had same challenge with two different bar buttons which I needed to hide and show on some conditions. First was "edit" button to enter editing mode on my view and second was "done" to save changes. So they had to appear interchangeably.
I made an extenstion to UINavigationItem class where implemented two methods. One to delete item from bar. Second to add item to bar. As a parameter to these methods I passed IBOutlets of the items which I made strong so they would not deallocate when item is removed from bar
extension UINavigationItem {
func deleteFromRightBar(item: UIBarButtonItem) {
// make sure item is present
guard let itemIndex = self.rightBarButtonItems?.index(of: item) else {
return
}
// remove item
self.rightBarButtonItems?.remove(at: itemIndex)
}
func addToRightBar(item: UIBarButtonItem) {
// make sure item is not present
guard self.rightBarButtonItems?.index(of: item) == nil else {
return
}
// add item
self.rightBarButtonItems?.append(item)
}
}
I used these methods like that
navigationItem.deleteFromRightBar(item: doneButton)
navigationItem.addToRightBar(item: editButton)

Resources