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
}
Related
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.
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.
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
}
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.
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.