Pin UIView to top of screen when keyboard slides up - ios

I've got the following UIView Layout for a simple chat to the left and I'm shifting the screen content up by the size of the keyboard, when the keyboard shows up, like shown to the right.
#objc func keyboardWillShow(_ notification: Notification) {
if let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue {
if self.view.frame.origin.y == 0 { self.view.frame.origin.y -= keyboardSize.height }
}
}
What I would like to achieve is to pin the upper part of my layout (i.e. the user profilepic, username, onine / offline indicator) to the screen, so that it stays in pace and only the UITableView get shifted up by the size of the keyboard.
How can I achieve that?

You are able to move your message content (bottom view) and decrease the height of your tableView the amount equal to keyboard height.
More detail, you constraint your message content and assign to bottomConstraint, your table height constraint is heightConstraint. When keyboard shows, decrease bottomConstraint and heightConstraint by the keyboard height; revert when keyboard hides.
Hope this will be helpful.

Related

Black color appears when moving origin to top when keyboard pops

I am moving the origin of the self.view by the -minY of the text field when the keyboard shows to make the text field show at top of the screen. But if the text field is near the bottom, the frame bottom gets black color. I tapped on the first text field in the attached image which moves it to top. Is there a way to move the view without displaying black color?
The view hierarchy is
+ UIViewController
+ UIView
+ UIScrollView
+ UIView
- UITextField
- UITextField
- UITextField
self.view.frame.origin.y = -(tagsTextField.frame.minY)
I fixed it by setting scrollview content offset on textfield tap.
scrollView.setContentOffset(CGPoint(x: 0, y: tagsTextField.frame.minY), animated: true)
And resetting on keyboard dismiss.
scrollView.setContentOffset(CGPoint(x: 0.0, y: 0.0), animated: true)
You can move frame up when keyboard frame hides your textfield by the height of keyboard:
override func keyboardWillBeShown(note: Notification) {
let userInfo = note.userInfo
let keyboardFrame = userInfo?[UIKeyboardFrameEndUserInfoKey] as! CGRect
if keyboardFrame.intersects(activeTextField.frame) {
UIView.animate(withDuration: 0.2) {
self.view.frame.origin.y = -keyboardFrame.height
}
}
}
override func keyboardWillBeHidden(note: Notification) {
UIView.animate(withDuration: 0.2) {
self.view.frame.origin.y = 0
}
}
You can set the background color of the embedding view (perhaps your navigation controller) the same color as your view background color.
What I usually do in form screen is I put my input fields in a UITableView. Then, instead of moving the view up to make the current input field visible, I scroll the UITableView.
So, the background view/color of the UITableView does not move.

iOS Swift3 Move a Textfield and a ContainerView with the keyboard

I need help to make my chat ViewController behave like Whatsapp does when you open and close your keyboard. That means: If the keyboard is opened, the view moves up. If it is closed, it returns to the original position.
What I already have: My chat ViewController has a ContainerView and below that ContainerView is a TextField. I have set up two observers which are catching UIKeyboardWillShow and UIKeyboardWillHide. The ContainerView and the TextField both have constraints at the top, left, right and bottom. between them is a vertical spacing.
What I've tried: I took the bottom constraint of the TextField and added the height of the keyboard to it. It moves correctly.
Where I need help: The TableView inside the ContainerView does not move correctly with the TextField. Here are some screenshots that show my problem: http://imgur.com/a/zd7mw
it looks like the keyboard is overlaying the ContainerView. But it seems like the TableView inside the ContainerView is messing it up.
I expect "thank you for reading the example messages" to be shown when I click the TextField, but it gets cut. I thought about scrolling down to the bottom everytime you open the keyboard, but that would not work if you look into the middle of the chat and try to type something, you want to stay exactly where you are looking at.
Thank you for reading through this and thanks for any help!
There are two ways to do what you are trying to accomplish:
programmatically modify the bottom constraint constant to include the height of the keyboard. This will pin the bottom of your tableview to the top.
The method I would use is just adding a content inset to your tableview equal to the height of the keyboard. This approach is more simple because it does not require you modifying view constraint constants.
Start like this:
override func viewDidLoad(){
NotificationCenter.default.addObserver(self, selector: #selector(keyboardHide), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardShow), name: NSNotification.Name.UIKeyboardWillChangeFrame, object: nil)
}
func keyboardShow(notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
self.tableView.contentInset = UIEdgeInsetsMake(0, 0, keyboardSize.height, 0)
}
}
func keyboardHide(notification: NSNotification) {
self.tableView.contentInset = UIEdgeInsetsMake(0, 0, 0, 0)
}
Note that if you want to push the content up to the bottom of the textfield, add your textfield height to your keyboard height.

Prevent UITextView from auto-scrolling when when being resized through auto-layout

My view has a TextView with 0 as the number of lines, populated with a long text that the user can edit.
When the keyboard shows, to resize the TextView so that it fits in the visible portion of the screen, the height constraint of an empty view at the bottom of the screen is equaled to the keyboard height. The bottom of the textView is constrained to the top of the empty view, so the text view gets resized.
When this happens, the textView automatically scrolls down in the text. I would like the text to avoid this scrolling, so that the first line always stays visible. I have tried a few ways, such as:
Disabling scrolling between keyboardWillShow and keyboardDidShow, not working
Scrolling back to zero, but we can see the text scrolling down and then scrolling back up (textView.scrollRangeToVisible(NSRange(location:0, length:0)))
Here's a drawing to make it much clearer (can't embed it yet, sorry):
UITextView Drawing
Relevant code:
func keyboardWillShow(_ notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue {
let duration: TimeInterval = (notification.userInfo?[UIKeyboardAnimationDurationUserInfoKey] as! NSNumber).doubleValue
emptyViewHeightConstraint.constant = keyboardSize.height
UIView.animate(withDuration: duration) { self.view.layoutIfNeeded() }
}
}
func keyboardWillHide(_ notification: NSNotification) {
let duration: TimeInterval = (notification.userInfo?[UIKeyboardAnimationDurationUserInfoKey] as! NSNumber).doubleValue
emptyViewHeightConstraint.constant = 0
UIView.animate(withDuration: duration) { self.view.layoutIfNeeded() }
}
PS: The TextView is actually inside a StackView, and this StackView has its bottom constrained to the top of the empty view.

Showing Textfield when Keyboard is up

I understand that the issue I'm posting has been discussed a lot but as far as I have searched in SO, I couldn't find a solution specific to my issue. I'm posting this to get some inputs.
Problem:
View hierarchy: View -> ScrollView -> View -> Textfield
I have implemented two screens with multiple textfields laid on top of a scrollview to avoid keyboard obscuring the textfield when its at the bottom of the page. I have achieved this using the sample code provided by Apple with minor modifications.
Subscribe to UIKeyboardWillShowNotification and UIKeyboardWillHideNotification
When keyboard is shown, get the height of the keyboard from userInfo dictionary and set appropriate content inset to move the textfield above the Keyboard
When keyboard is hidden, set the content inset to UIEdgeInsetZero to bring back to normal size
The above implementation works perfectly fine when there are more textfields that could occupy the entire screen (assume there are 10 textfields and they extend beyond the frame of the the scroll view). Whenever I tap on a textfield, it moves above the keyboard as expected and I can also scroll the page till the bottom most textfield is visible above the keyboard frame (when the keyboard is still up).
My problem arises when I have only two textfield in the scrollview that are centered vertically in the screen. When I tap on a textfield, the scrollview get an inset equivalent to the keyboard height and it moves above the keyboard as expected but when I scroll the screen, I could see a huge blank space (due to the additional inset) below the textfields.
How can I avoid that blank space when there are only one or two textfields in the page? Should I have to write a different logic to handle that scenario? Any help would be appreciated.
Below is the code:
override func viewDidLoad() {
super.viewDidLoad()
NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardWillShow:", name: UIKeyboardWillShowNotification, object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardWillHide:", name: UIKeyboardWillHideNotification, object: nil)
}
func keyboardWillShow(notification: NSNotification) {
if let userInfo = notification.userInfo {
let keyboardFrame = (userInfo[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.CGRectValue()
if let kbFrame = keyboardFrame {
let inset = CGRectGetHeight(kbFrame)
scrollView.contentInset = UIEdgeInsetsMake(0, 0, inset, 0)
scrollView.scrollIndicatorInsets = UIEdgeInsetsMake(0, 0, inset, 0)
}
}
}
func keyboardWillHide(notfication: NSNotification) {
scrollView.contentInset = UIEdgeInsetsZero
scrollView.scrollIndicatorInsets = UIEdgeInsetsZero
}
TPKeyboardAvoidingScrollView is good, but i recently switched to IQKeyboardManager -> https://github.com/hackiftekhar/IQKeyboardManager
Its upto you to choose but i prefer IQKeyboardManager cause its easy
There are a number of solutions out there, but my preferred solution is to use TPKeyboardAvoiding. To use it here you would just make your UIScrollView an instance or subclass of TPKeyboardAvoidingScrollView in SB or code and it should care of the rest, no other code required!

UITableView contentInset not updating when keyboard interactively dismissed

Update
I found that in my many refactorings I was inheriting from UIViewController instead of UITableViewController, so I was missing some automatic behaviours that UITableViewController provides. However, I still needed to manually handle the scroll views insets when the keyboard was being interactively dismissed. See my updated answer.
I am trying to emulate iMessage in how the keyboard is dismissed when the user drags it to the bottom of the screen. I have it working with one small visual issue that's bugging me.
As the keyboard is dragged off the screen the scroll indicators do not resize correctly - that is until it has been completely dismissed.
I use keyboard notifications to tell me when the keyboard has appeared to increase the content and scroll insets by the height of the keyboard. It seems I didn't need to do anything when the keyboard has been dismissed as the insets appear to be correct when it has been. However when dismissing interactively I can't update the insets during the dragging event.
To illustrate the issue, the first image shows that content has scrolled off the top of the screen due to the space being occupied by the keyboard; the user has scrolled to the last row in the table:
Here, the keyboard is being dismissed and is almost completely off-screen. However notice how the scroll indicators are completely the wrong size. All of the content is now almost on screen so the indicators should be stretching, however, what happens is that as the keyboard moves down, the scroll indicators move up and do not stretch. This is not what happens in iMessage.
I think what I'm doing is pretty standard, I'm creating a UIToolBar (iOS 8.3) and overriding these methods in my view controller:
override var inputAccessoryView: UIView {
return toolbar
}
override func canBecomeFirstResponder() -> Bool {
return true
}
func willShowKeyboard(notification: NSNotification) {
let keyboardFrame = notification.userInfo![UIKeyboardFrameEndUserInfoKey] as! NSValue
tableView.contentInset.bottom = keyboardFrame.CGRectValue().height
tableView.scrollIndicatorInsets.bottom = keyboardFrame.CGRectValue().height
}
Update
After switching to a UITableViewController, I found that this implementation of scrollViewDidScroll() (along with the other methods in the original solution below) did the trick of dynamically resizing the insets when the keyboard was interactively dismissed.
override func scrollViewDidScroll(scrollView: UIScrollView) {
if !keyboardShowing {
return
}
let toolbarFrame = toolbar.convertRect(toolbar.frame, toView: nil)
tableView.scrollIndicatorInsets.bottom = view.bounds.height - toolbarFrame.minY
tableView.contentInset.bottom = view.bounds.height - toolbarFrame.minY
}
I've managed to achieve the same effect. I'm not sure if this is the correct method, but it works nicely. I'll be interested to know what other solutions there might be.
func didShowKeyboard(notification: NSNotification) {
let keyboardFrame = notification.userInfo![UIKeyboardFrameEndUserInfoKey] as! NSValue
let keyboardHeight = keyboardFrame.CGRectValue().height
tableView.contentInset.bottom = keyboardHeight
tableView.scrollIndicatorInsets.bottom = keyboardHeight
keyboardShowing = true
}
func didHideKeyboard(notification: NSNotification) {
keyboardShowing = false
}
func scrollViewDidScroll(scrollView: UIScrollView) {
if !keyboardShowing {
return
}
let toolbarFrame = view.convertRect(toolbar.frame, fromView: toolbar)
tableView.scrollIndicatorInsets.bottom = view.bounds.height - toolbarFrame.minY
tableView.contentInset.bottom = view.bounds.height - toolbarFrame.minY
}

Resources