Move scrollview to original state - ios

I want to move up the view when the keyboard appears and move the view back when the keyboard disappears. I use an scrollview for this.
I have this almost working. The bug that I have is this:
You trigger the keyboard to show
You trigger the keyboard to hide
Things are going great so far.
You trigger the keyboard to show
You trigger the keyboard to hide <-- This doesn't work anymore, but the first time it works perfectly.
The code when the keyboard shows up (this works overtime):
func keyboardWasShown(notification: NSNotification) {
var userInfo = notification.userInfo!
let keyboardFrame:CGRect = (userInfo[UIKeyboardFrameBeginUserInfoKey] as! NSValue).cgRectValue
let buttonOrigin: CGPoint = btn.frame.origin
let buttonHeight: CGFloat = btn.frame.size.height
var visibleRect: CGRect = view.frame
visibleRect.size.height -= keyboardFrame.size.height
if !visibleRect.contains(buttonOrigin) {
let scrollPoint = CGPoint(x: CGFloat(0.0), y: CGFloat(buttonOrigin.y - visibleRect.size.height + (buttonHeight + 8)))
scrollView.setContentOffset(scrollPoint, animated: true)
}
}
The code when the keyboard is going to hide (This code works the first time, it doesn't work the second time):
func keyboardWillBeHidden(notification: NSNotification){
var info = notification.userInfo!
let keyboardSize = (info[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue.size
let contentInsets : UIEdgeInsets = UIEdgeInsetsMake(0.0, 0.0, -keyboardSize!.height, 0.0)
self.scrollView.contentInset = contentInsets
self.scrollView.scrollIndicatorInsets = contentInsets
self.view.endEditing(true)
self.scrollView.isScrollEnabled = false
}
This is how my view looks like:
Everything needs to move up when the keyboard is showing. Moving up works:
But when the keyboard disappears it doesn't work:

Just change this Line
let contentInsets : UIEdgeInsets = UIEdgeInsetsMake(0.0, 0.0, -keyboardSize!.height, 0.0)
with
let contentInsets : UIEdgeInsets = .zero
or try This
//MARK:- Keyboard Methods
func keyboardWillOpen(sender:Notification) {
if let info = sender.userInfo{
if let keyboardSize = info[UIKeyboardFrameBeginUserInfoKey] as? CGRect{
let contentInsets = UIEdgeInsetsMake(0, 0, -keyboardSize.height, 0)
self.scrollView.contentInset = contentInsets
self.scrollView.scrollIndicatorInsets = contentInsets
}
}
}
func keyboardWillHide(notification:Notification) {
if let info = notification.userInfo {
if let keyboardSize = info[UIKeyboardFrameBeginUserInfoKey] as? CGRect {
self.scrollView.contentInset = .zero
self.scrollView.scrollIndicatorInsets = .zero
}
}
}

I would not bother to scroll the scrollview, but instead move the scrollview itself.
Get the outlet of the scrollview bottom constraint, and, supposing the scrollview is fullscreen, set the constraint to zero when the keyboard is hidden, and then set it to the keyboard height (correct value is in the notification of keyboardwillshow).
Pseudo code looks like this :
func keyboardWillShow(notification: NSNotification) {
self.scrollviewBottomConstraint.constant = notification.keyboardheight;
self.layoutifneeded(); //I suggest you put this in a 0.3 second animation block
}
func keyboardWillHide(notification: NSNotification) {
self.scrollviewBottomConstraint.constant = 0;
self.layoutIfNeeded(); //I suggest you put this in a 0.3 second animation block
}

Related

Swift iOS - Scroll view not scrolling properly

I have got a textView and a scrollView. I want to achieve a state where I click in the textView and it will make way for the keyboard. However it seems to work only when I click on the textView for the second time.
And the code:
#objc open override func keyboardWillShow(notification: NSNotification) {
keyboardTapGesture.isEnabled = true
scrollView.isScrollEnabled = true
let info = notification.userInfo!
let keyboardSize = (info[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue.size
let contentInsets : UIEdgeInsets = UIEdgeInsets(top: 0.0, left: 0.0, bottom: keyboardSize?.height ?? 0.0, right: 0.0)
scrollView.contentInset = contentInsets
scrollView.scrollIndicatorInsets = contentInsets
scrollView.scrollRectToVisible(commentInput.frame, animated: true)
}
#objc open override func keyboardWillHide(notification: NSNotification) {
keyboardTapGesture.isEnabled = false
scrollView.contentInset = .zero
scrollView.scrollIndicatorInsets = .zero
view.endEditing(true)
scrollView.isScrollEnabled = false
}
Thanks for your help.

UIScrollView scroll to button when keyboard is visible

I got a pretty basic login form in my iOS application. The typical behavior I normally implement ist registering for the keyboard notifications and handling the scroll behavior of the UIScrollView in order to show the currently focused UITextField. However this time I need a slightly different behavior.
If the keyboard will show I want to scroll the scrollview so the submit button will be presented right above the keyboard and will be visible.
I know the recommended way is to use contentInsets but this seems to fail for me. I got it working with setting the contentOffset but I would love to see a better solution to this problem.
Here is my current code:
func keyboardWillShow(notification: Notification) {
guard let userInfo: NSDictionary = notification.userInfo as NSDictionary?,
let keyboardInfo = userInfo[UIKeyboardFrameEndUserInfoKey] as? NSValue else {
return
}
let keyboardSize = keyboardInfo.cgRectValue.size
guard let loginButton = loginButton else {
return
}
let buttonOrigin: CGPoint = loginButton.convert(loginButton.bounds, to: self).origin
let buttonHeight: CGFloat = loginButton.bounds.size.height
var visibleRect = self.frame
visibleRect.size.height -= keyboardSize.height
if !visibleRect.contains(buttonOrigin) {
let scrollPoint = CGPoint(x: 0.0, y: buttonOrigin.y - visibleRect.size.height + buttonHeight)
scrollView.setContentOffset(scrollPoint, animated: true)
}
}
Did you try it the following way?
func keyboardWillShow(notification: Notification) {
self.scrollView.isScrollEnabled = true
let contentInsets = UIEdgeInsetsMake(0.0, 0.0, self.keyboardSize.height, 0.0)
self.scrollView.contentInset = contentInsets
self.scrollView.scrollIndicatorInsets = contentInsets
var rect = self.view.frame
rect.size.height -= self.keyboardSize.height
if !rect.contains(self.loginButton.frame.origin) {
self.scrollView.scrollRectToVisible(self.loginButton.frame, animated: true)
}
}
After hours of trying this is the way I do the scrolling in my applications.

UIKeyboard Notification.userInfo Key bug in Xcode 9

#objc func keyboardWasShown(_ notification:NSNotification) {
var userinfo = notification.userInfo!
let kbSize:NSValue = userinfo[UIKeyboardFrameBeginUserInfoKey] as! NSValue
let kbRectSize = kbSize.cgRectValue
let edgeInsects = UIEdgeInsetsMake(0, 0,kbRectSize.height + 10, 0)
self.scrollView.contentInset = edgeInsects
self.scrollView.scrollIndicatorInsets = edgeInsects
// active text field
var aRect:CGRect = self.view.frame
aRect.size.height -= kbRectSize.height
if(!aRect.contains(activeField.frame.origin)){
scrollView.isScrollEnabled = true
scrollView.scrollRectToVisible(activeField.frame, animated: true)
aRect = CGRect.zero
}
}
The scrollview will scroll for first time as intended and then becomes unresponsive.
The code was working fine until Xcode 8.3 without any issues.
please confirm whether it's a bug or not and how to circumvent it.Thanks in advance.
I too got the same issue and by doing some changes it is working fine. In your scenario try below code and see if it works:
#objc func keyboardWasShown(_ notification:NSNotification) {
var keyboardHeight: CGFloat = 260
var userinfo = notification.userInfo!
let keyboardSize = (userinfo[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue.size
if keyboardSize!.height > 10.0{
keyboardHeight = keyboardSize!.height
}
let edgeInsects = UIEdgeInsetsMake(0, 0, keyboardHeight + 10, 0)
self.scrollView.contentInset = edgeInsects
self.scrollView.scrollIndicatorInsets = edgeInsects
// active text field
var aRect:CGRect = self.view.frame
aRect.size.height -= kbRectSize.height
if(!aRect.contains(activeField.frame.origin)){
scrollView.isScrollEnabled = true
scrollView.scrollRectToVisible(activeField.frame, animated: true)
aRect = CGRect.zero
}
}
I have used below code in my project for sign up screen and it is working perfectly fine:
func keyboardWasShown(notification: NSNotification){
//Need to calculate keyboard exact size due to Apple suggestions
var keyboardHeight: CGFloat = 260
self.scrollView.isScrollEnabled = true
var info = notification.userInfo!
let keyboardSize = (info[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue.size
if keyboardSize!.height > 10.0{
keyboardHeight = keyboardSize!.height
}
let contentInsets : UIEdgeInsets = UIEdgeInsetsMake(0.0, 0.0, keyboardHeight, 0.0)
self.scrollView.contentInset = contentInsets
self.scrollView.scrollIndicatorInsets = contentInsets
var aRect : CGRect = self.view.frame
aRect.size.height -= keyboardHeight
if let activeField = self.activeField {
if (!aRect.contains(activeField.frame.origin)){
self.scrollView.scrollRectToVisible(activeField.frame, animated: true)
}
}
}
func keyboardWillBeHidden(notification: NSNotification){
//Once keyboard disappears, restore original positions
self.scrollView.contentInset = UIEdgeInsets.zero
self.scrollView.scrollIndicatorInsets = UIEdgeInsets.zero
self.scrollView.isScrollEnabled = false
}
Just try and debug with some more changes according to your requirements. Hope it works.
// ==== solution =====//
// ==== USE UIKeyboardFrameEndUserInfoKey ===
// as UIKeyboardFrameBeginUserInfoKey return height as 0 for some
//reason on second call onwards , some weird bug indeed.
var userinfo = notification.userInfo!
// === bug fix =====
let kbSize:NSValue = userinfo[UIKeyboardFrameEndUserInfoKey] as! NSValue
let kbRectSize = kbSize.cgRectValue
let edgeInsects = UIEdgeInsetsMake(0, 0,kbRectSize.height + 10, 0)
self.scrollView.contentInset = edgeInsects
self.scrollView.scrollIndicatorInsets = edgeInsects
// active text field
var aRect:CGRect = self.view.frame
aRect.size.height -= kbRectSize.height
if(!aRect.contains(activeField.frame.origin)){
scrollView.isScrollEnabled = true
scrollView.scrollRectToVisible(activeField.frame, animated: true)
aRect = CGRect.zero
}
Solution Found , in Xcode 9, there seems to be some change in key - value pairs of Keyboard notification userInfo dictionary.
Use UIKeyboardFrameEndUserInfoKey instead of UIKeyboardFrameBeginUserInfoKey,
As Value for UIKeyboardFrameBeginUserInfoKey for some reason does not return the proper size of the keyboard on second call onwards.

UIScrollView stops scrolling when keyboard is dismissed from UITextField

func keyboardWillShow(notification: NSNotification) {
//self.scrollView.frame.origin.y = -150
debugPrint("keyboard show")
self.scrollView.scrollEnabled = true
let info : NSDictionary = notification.userInfo!
let keyboardSize = (info[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue().size
let contentInsets : UIEdgeInsets = UIEdgeInsetsMake(0.0, 0.0, keyboardSize!.height, 0.0)
self.scrollView.contentInset = contentInsets
self.scrollView.scrollIndicatorInsets = contentInsets
var aRect : CGRect = self.view.frame
aRect.size.height -= keyboardSize!.height
if activeField != nil
{
if (!CGRectContainsPoint(aRect, activeField!.frame.origin))
{
self.scrollView.scrollRectToVisible(activeField!.frame, animated: true)
}
}
}
func keyboardWillHide(notification: NSNotification) {
// self.scrollView.frame.origin.y = 0
debugPrint("key board hide")
let info : NSDictionary = notification.userInfo!
let keyboardSize = (info[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue().size
let contentInsets : UIEdgeInsets = UIEdgeInsetsMake(0.0, 0.0, -keyboardSize!.height, 0.0)
self.scrollView.contentInset = contentInsets
self.scrollView.scrollIndicatorInsets = contentInsets
self.view.endEditing(true)
self.scrollView.keyboardDismissMode = .Interactive
self.viewDidLayoutSubviews()
}
I am calculating uiscrollview content size like this, container is the view inside uiscrollview
override func viewDidLayoutSubviews() {
var i = 0
var contentRect : CGRect = CGRectZero;
for view in self.container.subviews {
debugPrint("\(i)")
contentRect = CGRectUnion(contentRect, view.frame)
i += 1
}
self.scrollView.contentSize = contentRect.size;
}
After doing all this when the keyboard disappears scrollview stops scrolling. I am new to ios development and this uiscrollview is killing me.

scroll a text view up when keyboard is on screen

I have a method on a view controller which scrolls text fields up when focused if the keyboard is on screen.
I have tried to do the same thing on a different controller but with a textview but it's not working.
Attempt:
func keyboardOnScreen(notification: NSNotification){
// Retrieve the size and top margin (inset is the fancy word used by Apple)
// of the keyboard displayed.
let info: NSDictionary = notification.userInfo!
let kbSize = info.valueForKey(UIKeyboardFrameEndUserInfoKey)?.CGRectValue.size
let contentInsets: UIEdgeInsets = UIEdgeInsetsMake(0.0, 0.0, kbSize!.height, 0.0)
scrollView.contentInset = contentInsets
scrollView.scrollIndicatorInsets = contentInsets
var aRect: CGRect = self.view.frame
aRect.size.height -= kbSize!.height
//you may not need to scroll, see if the active field is already visible
print("are we in here?")
if activeField != nil {
print("active field was not nil")
if (CGRectContainsPoint(aRect, activeField!.frame.origin) == false) {
let scrollPoint:CGPoint = CGPointMake(0.0, activeField!.frame.origin.y - kbSize!.height)
scrollView.setContentOffset(scrollPoint, animated: true)
}
}
}
func keyboardOffScreen(notification: NSNotification){
print("keyboard off screen")
let contentInsets:UIEdgeInsets = UIEdgeInsetsZero
scrollView.contentInset = contentInsets
scrollView.scrollIndicatorInsets = contentInsets
self.scrollView.setContentOffset(CGPointMake(0, -self.view.frame.origin.y/2), animated: true)
}
My controller extends the text view delegate. Active field is declared as an optional. And it is set in the following:
func textViewDidBeginEditing(textView: UITextView) {
activeField = textView
//other
}
func textViewDidEndEditing(commentTextView: UITextView) {
activeField = nil
//other
}

Resources