UIScrollView stops scrolling when keyboard is dismissed from UITextField - ios

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.

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.

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.

Move scrollview to original state

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
}

UITextView jumps to the bottom of the screen, behind the keyboard when frame was updated

I am implementing an UITextView textInput to have a similar effect as the iOS message window, where the textview expand/shrink based on the text content.
When I update the UITextView frame in textViewDidChange function, the UITextView jumped to the bottom of the screen, and get hidden behind the keyboard. It doesn't appear in the specified frame.
Here's my code.
func textViewDidChange(textView: UITextView)
{
print ("textview did changed")
let fixedWidth : CGFloat = textView.frame.size.width
let newSize : CGSize = textView.sizeThatFits(CGSizeMake(fixedWidth, CGFloat(MAXFLOAT)))
var newFrame : CGRect = textView.frame
newFrame.size = CGSizeMake(CGFloat(fmaxf((Float)(newSize.width), (Float)(fixedWidth))),newSize.height)
textView.frame = newFrame
}
Also, here's the keyboardWillAppear() and keyboardWillDisapppear() function, which is working fine
func keyboardWillAppear(notification: NSNotification){
print ("keyboard will appear")
let userInfo:NSDictionary = notification.userInfo!
let keyboardSize:CGSize = userInfo.objectForKey(UIKeyboardFrameBeginUserInfoKey)!.CGRectValue().size
let contentInsets:UIEdgeInsets = UIEdgeInsetsMake(0, 0, keyboardSize.height, 0)
self.tableView.contentInset = contentInsets
self.tableView.scrollIndicatorInsets = contentInsets
scrollToBottomOfTheTable()
var messageFrame:CGRect = self.textInput.frame
messageFrame.origin.y -= keyboardSize.height
self.textInput.frame = messageFrame
}
func keyboardWillDisappear(notification: NSNotification) {
print ("keyboard will disappear")
let userInfo:NSDictionary = notification.userInfo!
let keyboardSize:CGSize = userInfo.objectForKey(UIKeyboardFrameBeginUserInfoKey)!.CGRectValue().size
UIView.beginAnimations(nil, context: nil)
UIView.setAnimationDuration(0.25)
self.tableView.contentInset = UIEdgeInsetsZero
UIView.commitAnimations()
self.tableView.scrollIndicatorInsets = UIEdgeInsetsZero
var messageFrame:CGRect = self.textInput.frame
messageFrame.origin.y += keyboardSize.height
self.textInput.frame = messageFrame
}
Please help! Thanks.

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