On selecting a textfield, adjust view height for keyboard - ios

I have a ViewController (login view) in xib which has 2 UITextField and 1 UIButton component. The view hierarchy is
UIView
-> UIScrollView
-> UIView (contentView)
-> UITextField
-> UITextField
-> UIButton
I am using autolayout for the view. The UIScrollView has 4 constraints i.e., leading, trailing, top & bottom aligned to its superview (UIViewController view). ContentView has 4 constraints i.e., leading, trailing, top & bottom to its superview (UIScrollView) and width matching UIViewController view.
I have followed the steps as in Apple documentation to scroll up or down when user selects UITextField so that keyboard is not on top of UITextField.
When keyboard is shown scrollview is scrolled up but when keyboard is hidden scrollview does not scroll back. Any help is appreciated.
override func viewDidLoad() {
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
}
func keyboardWillHide(notification: NSNotification) {
let contentInsets: UIEdgeInsets = UIEdgeInsets.zero
self.scrollView.contentInset = contentInsets
self.scrollView.scrollIndicatorInsets = contentInsets
}
func keyboardWillShow(notification: NSNotification) {
if let activeField = self.activeField, let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
let contentInsets = UIEdgeInsets(top: 0.0, left: 0.0, bottom: keyboardSize.height, right: 0.0)
self.scrollView.contentInset = contentInsets
self.scrollView.scrollIndicatorInsets = contentInsets
var aRect = self.view.frame
aRect.size.height -= keyboardSize.size.height
if (!aRect.contains(activeField.frame.origin)) {
self.scrollView.scrollRectToVisible(activeField.frame, animated: true)
}
}
}

This problem with the keyboard is explained in detail in the apple documentation:
https://developer.apple.com/library/content/documentation/StringsTextFonts/Conceptual/TextAndWebiPhoneOS/KeyboardManagement/KeyboardManagement.html
Alternatively, try the following code:
func registerForKeyboardNotifications(){
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWasShown(notification:)), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillBeHidden(notification:)), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
}
func deregisterFromKeyboardNotifications(){
NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillShow, object: nil)
NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillHide, object: nil)
}
func keyboardWasShown(notification: NSNotification){
self.scrollView.isScrollEnabled = true
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
var aRect : CGRect = self.view.frame
aRect.size.height -= keyboardSize!.height
if let activeField = self.activeField {
if (!aRect.contains(activeField.frame.origin)){
self.scrollView.scrollRectToVisible(activeField.frame, animated: true)
}
}
}
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
}
func textFieldDidBeginEditing(_ textField: UITextField){
activeField = textField
}
func textFieldDidEndEditing(_ textField: UITextField){
activeField = nil
}

The scrollView contentInset.top was not 0.0 hence i was facing the problem. After changing contentInset.top properly scrollView set to original position after keyboard was hidden.
func keyboardWillShow(notification: NSNotification) {
self.scrollView.isScrollEnabled = true
var info = notification.userInfo!
let keyboardSize = (info[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue.size
let contentInsets : UIEdgeInsets = UIEdgeInsetsMake(self.scrollView.contentInset.top, 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 let activeField = self.activeField {
if (!aRect.contains(activeField.frame.origin)){
self.scrollView.scrollRectToVisible(activeField.frame, animated: true)
}
}
}

Related

extension for keyboard show notification iOS

I'm using this code. if keyboard appears then it increases the size of the view so user can easily scroll to the bottom. everything works fine but I want to make an extension for this code because I don't want to use such a long code in my controller
import UIKit
extension UIViewController {
func hideKeyboardWhenTappedAround() {
let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(dismissKeyboard))
tap.cancelsTouchesInView = false
view.addGestureRecognizer(tap)
}
#objc func dismissKeyboard() {
view.endEditing(true)
}
func setnotification()
{
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWasShown), name: UIResponder.keyboardWillShowNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillBeHidden(notification:)), name: UIResponder.keyboardWillHideNotification, object: nil)
}
#objc func keyboardWasShown(notification: NSNotification)
{
var info = notification.userInfo!
let keyboardSize = (info[UIResponder.keyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue.size
let contentInsets : UIEdgeInsets = UIEdgeInsets(top: 0.0, left: 0.0, bottom: keyboardSize!.height+10, right: 0.0)
self.scrolView.contentInset = contentInsets
self.scrolView.scrollIndicatorInsets = contentInsets
var aRect : CGRect = self.view.frame
aRect.size.height -= keyboardSize!.height
if let activeField = activeTextField
{
if (!aRect.contains(activeField.frame.origin))
{
self.scrolView.scrollRectToVisible(activeField.frame, animated: true)
}
}
}
// when keyboard hide reduce height of scroll view
#objc func keyboardWillBeHidden(notification: NSNotification){
let contentInsets : UIEdgeInsets = UIEdgeInsets(top: 0.0, left: 0.0,bottom: 0.0, right: 0.0)
myScrolView!.contentInset = contentInsets
myScrolView!.scrollIndicatorInsets = contentInsets
self.view.endEditing(true)
}
}
Extensions add new functionality to an existing class, structure, enumeration, or protocol type.
Syntax for extension:
extension ViewControllerName {
// Put code which you want to
}
/**
If you implement UITableViewDataSource and UITableViewDelegate methods
or you can implement for UIPickerViewDataSource methods and protocol also
*/
extension ViewController: UItableViewDataSource, UITableViewDelegate {
//implement tableview datasource and delegate method
}
/**
Keyboard show/hide
*/
extension ViewController {
/**
Add scrollview functionality to scroll top and you can call
this function from anywhere in the controller.
*/
func scrollToTop() {
}
#objc func keyboardWasShown(notification: NSNotification)
{
var info = notification.userInfo!
let keyboardSize = (info[UIResponder.keyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue.size
let contentInsets : UIEdgeInsets = UIEdgeInsets(top: 0.0, left: 0.0, bottom: keyboardSize!.height+10, right: 0.0)
self.scrolView.contentInset = contentInsets
self.scrolView.scrollIndicatorInsets = contentInsets
var aRect : CGRect = self.view.frame
aRect.size.height -= keyboardSize!.height
if let activeField = self.activeTextField
{
if (!aRect.contains(activeField.frame.origin))
{
self.scrolView.scrollRectToVisible(activeField.frame, animated: true)
}
}
}
// when keyboard hide reduce height of scroll view
#objc func keyboardWillBeHidden(notification: NSNotification){
let contentInsets : UIEdgeInsets = UIEdgeInsets(top: 0.0, left: 0.0,bottom: 0.0, right: 0.0)
self.scrolView.contentInset = contentInsets
self.scrolView.scrollIndicatorInsets = contentInsets
self.view.endEditing(true)
}
}

Move up view when textView show keyboard not working

i have an one textView and textfield in storyboard. i want to move up scroll view when textfield or textView show keyboard. every thing work fine for UITextField but for textView thats not working. there is my code for moving up view:
func keyboardWillShow(notification: NSNotification) {
if let endFrame = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue {
var keyboardHeight = view.bounds.height - endFrame.origin.y
if #available(iOS 11, *) {
if keyboardHeight > 0 {
keyboardHeight = keyboardHeight - view.safeAreaInsets.bottom
var contentInset:UIEdgeInsets = self.scrollView.contentInset
contentInset.bottom = keyboardHeight
viewBottom.constant = keyboardHeight
self.scrollView.contentInset = contentInset
}
}
else {
let keyboardSize = (notification.userInfo![UIKeyboardFrameEndUserInfoKey] as! NSValue).cgRectValue.size
var contentInset:UIEdgeInsets = self.scrollView.contentInset
contentInset.bottom = (keyboardSize.height)
self.scrollView.contentInset = contentInset
}
view.layoutIfNeeded()
}
}
func keyboardWillHide(notification: NSNotification) {
var contentInset:UIEdgeInsets = self.scrollView.contentInset
contentInset.bottom = 0
self.scrollView.contentInset = contentInset
}
what is difference between textfield and textView for showing keyboard?
when i tap textview that's method called and scroll view content inset same as when tap on textfield but view don't move up.
how can solve this problem?
class matchViewController: UIViewController,UITextViewDelegate {
var activeTextview: UITextView?
#IBOutlet weak var yourTextview: UITextView!
#IBOutlet weak var scrollView: UIScrollView!
override func viewDidLoad() {
super.viewDidLoad()
yourTextview.delegate=self
NotificationCenter.default.addObserver(self, selector: #selector(yourViewController.keyboardWillShow(_:)), name:NSNotification.Name.UIKeyboardWillShow, object: self.view.window)
NotificationCenter.default.addObserver(self, selector: #selector(yourViewController.keyboardWillHide(_:)), name:NSNotification.Name.UIKeyboardWillHide, object: self.view.window)
}
#objc func keyboardWillShow(_ sender: Notification) {
var info: [AnyHashable : Any] = sender.userInfo!
let kbSize: CGSize = ((info[UIKeyboardFrameEndUserInfoKey]! as AnyObject).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
if let activeTextview=activeTextview{
self.scrollView.scrollRectToVisible((activeTextview.frame), animated: true)
}
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?){
view.endEditing(true)
super.touchesBegan(touches, with: event)
}
#objc func keyboardWillHide(_ sender: Notification) {
let userInfo: [AnyHashable: Any] = sender.userInfo!
let keyboardSize: CGSize = (userInfo[UIKeyboardFrameBeginUserInfoKey]! as AnyObject).cgRectValue.size
self.scrollView.frame.origin.y += keyboardSize.height
}
override func viewDidDisappear(_ animated: Bool) {
NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillShow, object: self.view.window)
NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillHide, object: self.view.window)
}
func textViewDidBeginEditing(_ textView: UITextView) {
activeTextview=textView
}
func textViewDidEndEditing(_ textView: UITextView){
activeTextview=nil
}
}

UIScrollView scroll up on Keyboard

Problem Statement : I have Nib file of UIView contains UIScrollview, In scrollview I have several TextFields and a TextView at the Bottom. What I want is to scroll upward when textfield or Textview starts editing.
What I tried :
In custome Method
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWasShown(notification:)), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
I am calling this method parent view.
Notification Handling:
func keyboardWasShown(notification: NSNotification)
{
var userInfo = notification.userInfo!
var keyboardFrame:CGRect = (userInfo[UIKeyboardFrameBeginUserInfoKey] as! NSValue).cgRectValue
keyboardFrame = self.convert(keyboardFrame, from: nil)
var contentInset:UIEdgeInsets = self.mainScroll.contentInset
contentInset.bottom = keyboardFrame.size.height
self.mainScroll.contentInset = contentInset
}
This is working perfectly for UITextFields, But not working UITextView. Any idea where is the mistake.
PS: I have set the Delegates of UITextField and UITextView as well.
Any help will be much appreciated.
Replace keyboardWasShown function with the below function :
func keyboardWasShown(notification: NSNotification)
{
var userInfo = notification.userInfo!
var keyboardFrame:CGRect = (userInfo[UIKeyboardFrameBeginUserInfoKey] as! NSValue).cgRectValue
keyboardFrame = self.convert(keyboardFrame, from: nil)
self.mainScroll.contentOffset = CGPoint(x: 0, y: keyboardFrame.size.height - Any number that fits your need.)
}
In keyBoardWillHide :
self.mainScroll.contentOffset = CGPoint(x: 0, y: 0)
Hope it will help.
Happy Coding!
Demo link : https://github.com/harshilkotecha/UIScrollViewWhenKeyboardAppearInSwift3
when you have multiple textview it is so difficult so best solution ->
step 1 : add UITextFieldDelegate
class ScrollViewController: UIViewController,UITextFieldDelegate {
step 2 :create new IBOutlet but don't connect with any text field
// get current text box when user Begin editing
#IBOutlet weak var activeTextField: UITextField?
step 3 : write this two method when user focus on text filed object pass the reference and store in activeTextField
// get current text field
func textFieldDidBeginEditing(_ textField: UITextField)
{
activeTextField=textField;
}
func textFieldDidEndEditing(_ textField: UITextField)
{
activeTextField=nil;
}
step 5 : set Notification in viewdidload setNotificationKeyboard
override func viewWillAppear(_ animated: Bool) {
// call method for keyboard notification
self.setNotificationKeyboard()
}
// Notification when keyboard show
func setNotificationKeyboard () {
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWasShown(notification:)), name: .UIKeyboardWillShow, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillBeHidden(notification:)), name: .UIKeyboardWillHide, object: nil)
}
step 6 : two methods for hide and show keyboard
func keyboardWasShown(notification: NSNotification)
{
var info = notification.userInfo!
let keyboardSize = (info[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue.size
let contentInsets : UIEdgeInsets = UIEdgeInsetsMake(0.0, 0.0, keyboardSize!.height+10, 0.0)
self.scrollView.contentInset = contentInsets
self.scrollView.scrollIndicatorInsets = contentInsets
var aRect : CGRect = self.view.frame
aRect.size.height -= keyboardSize!.height
if let activeField = self.activeTextField
{
if (!aRect.contains(activeField.frame.origin))
{
self.scrollView.scrollRectToVisible(activeField.frame, animated: true)
}
}
}
// when keyboard hide reduce height of scroll view
func keyboardWillBeHidden(notification: NSNotification){
let contentInsets : UIEdgeInsets = UIEdgeInsetsMake(0.0, 0.0,0.0, 0.0)
self.scrollView.contentInset = contentInsets
self.scrollView.scrollIndicatorInsets = contentInsets
self.view.endEditing(true)
}
Swift 5
NotificationCenter.default.addObserver(self, selector: #selector(keyboardNotification), name: UIResponder.keyboardDidChangeFrameNotification, object: nil)
#objc func keyboardNotification(_ notification: Notification) {
if let userInfo = (notification as NSNotification).userInfo {
let endFrame = (userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue
let duration:TimeInterval = (userInfo[UIResponder.keyboardAnimationDurationUserInfoKey] as? NSNumber)?.doubleValue ?? 0
let animationCurveRawNSN = userInfo[UIResponder.keyboardAnimationCurveUserInfoKey] as? NSNumber
let animationCurveRaw = animationCurveRawNSN?.uintValue ?? UIView.AnimationOptions().rawValue
let animationCurve:UIView.AnimationOptions = UIView.AnimationOptions(rawValue: animationCurveRaw)
if (endFrame?.origin.y)! >= UIScreen.main.bounds.size.height {
scrollViewBottomConstraint?.constant = 0
} else {
if tabBarController?.tabBar.frame == nil {
return
}
scrollViewBottomConstraint?.constant = endFrame!.size.height - (tabBarController?.tabBar.frame.height)!
let bottomOffset = CGPoint(x: 0, y: 0)
scrollView.setContentOffset(bottomOffset, animated: true)
}
UIView.animate(withDuration: duration, delay: 0, options: animationCurve, animations: { self.view.layoutIfNeeded() }, completion: nil)
}
}
usage:
1.connect the scrollView outlet to your controller and set its name to 'scrollView'
2.connect the scrollView bottom constraint to your controller & set its name to 'scrollViewBottomConstraint'
3.put the notification observer to your ViewDidLoad function.

Cannot move Keyboard up when UITextView is clicked

I try to make UITextField and UITextView moving up when a keyboard pops out.
However, I find that it works only for UITextField but not for UITextView.
Is there anything I missed?
Both textFields and textViews are delegated.
Here's my code:
var activeField: UITextField?
var activeView: UITextView?
func registerForKeyboardNotifications()
{
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(ViewController.keyboardWasShown(_:)), name: UIKeyboardWillShowNotification, object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(ViewController.keyboardWillBeHidden(_:)), name: UIKeyboardWillHideNotification, object: nil)
}
func deregisterFromKeyboardNotifications()
{
NSNotificationCenter.defaultCenter().removeObserver(self, name: UIKeyboardWillShowNotification, object: nil)
NSNotificationCenter.defaultCenter().removeObserver(self, name: UIKeyboardWillHideNotification, object: nil)
}
func keyboardWasShown(notification: NSNotification)
{
var info : NSDictionary = notification.userInfo!
var keyboardSize = (info[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue().size
var 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 let activeFieldPresent = activeField
{
if (!CGRectContainsPoint(aRect, activeField!.frame.origin))
{
self.scrollview.scrollRectToVisible(activeField!.frame, animated: true)
}
}
if let activeViewPresent = activeView
{
if (!CGRectContainsPoint(aRect, activeView!.frame.origin))
{
self.scrollview.scrollRectToVisible(activeView!.frame, animated: true)
}
}
}
func keyboardWillBeHidden(notification: NSNotification)
{
var info : NSDictionary = notification.userInfo!
var keyboardSize = (info[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue().size
var contentInsets : UIEdgeInsets = UIEdgeInsetsZero
self.scrollview.contentInset = contentInsets
self.scrollview.scrollIndicatorInsets = contentInsets
self.view.endEditing(true)
}
func textFieldDidBeginEditing(textField: UITextField)
{
activeField = textField
registerForKeyboardNotifications()
}
func textFieldDidEndEditing(textField: UITextField)
{
deregisterFromKeyboardNotifications()
activeField = nil
}
func textViewDidBeginEditing(textView: UITextView) {
activeView = textView
registerForKeyboardNotifications()
}
func textViewDidEndEditing(textView: UITextView) {
deregisterFromKeyboardNotifications()
activeView = nil
}

Error on clicking UITextView unknown reason

I have a registration screen of textFields & textViews placed on a scroll view. For moving the view above text field I used the following codes -
mark: I have connected all the delegates properly.
var activeTextField:UITextField?
viewDidLoad()
{
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWasShown:"), name: UIKeyboardWillShowNotification, object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillBeHidden:"), name: UIKeyboardWillHideNotification, object: nil)
}
override func viewWillDisappear(animated: Bool)
{
NSNotificationCenter.defaultCenter().removeObserver(self, name: UIKeyboardDidHideNotification, object: nil)
NSNotificationCenter.defaultCenter().removeObserver(self, name: UIKeyboardWillHideNotification, object: nil)
}
func textFieldDidBeginEditing(textField: UITextField)
{
self.activeTextField = textField
}
func textFieldDidEndEditing(textField: UITextField)
{
self.activeTextField = nil
}
func textViewDidBeginEditing(textView: UITextView)
{
textView.becomeFirstResponder()
self.scrollView.setContentOffset(CGPointMake(0, 180), animated: true)
}
func textViewDidEndEditing(textView: UITextView)
{
self.scrollView.setContentOffset(CGPointMake(0, 0), animated: true)
textView.resignFirstResponder()
}
func keyboardWasShown(notification: NSNotification)
{
// Step 1: Get the size of the keyboard.
let info : NSDictionary = notification.userInfo!
let keyboardSize = (info.objectForKey(UIKeyboardFrameBeginUserInfoKey)?.CGRectValue as CGRect!).size
// Step 2: Adjust the bottom content inset of your scroll view by the keyboard height.
let contentInsets: UIEdgeInsets = UIEdgeInsetsMake(0.0, 0.0, keyboardSize.height, 0.0)
self.scrollView.contentInset = contentInsets
self.scrollView.scrollIndicatorInsets = contentInsets
// Step 3: Scroll the target text field into view.
var aRect: CGRect = self.view.frame
aRect.size.height -= keyboardSize.height
if !CGRectContainsPoint(aRect, activeTextField!.frame.origin)
{
let scrollPoint: CGPoint = CGPointMake(0.0, activeTextField!.frame.origin.y - (keyboardSize.height - 15))
self.scrollView.setContentOffset(scrollPoint, animated: true)
}
}
func keyboardWillBeHidden(notification: NSNotification)
{
let contentInsets: UIEdgeInsets = UIEdgeInsetsZero
self.scrollView.contentInset = contentInsets
self.scrollView.scrollIndicatorInsets = contentInsets
}
But as soon as I click on the textView I get an error message as -
But there is no error related breaks in my viewController. So what might be the error?

Resources