I'm using the following code to solve the common issue when Keyboard overlaps UITextField in iOS:
#IBOutlet var scrollView: UIScrollView!
var activeField: UITextField?
override func viewWillAppear(_ animated:Bool) {
super.viewWillAppear(animated)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide(_:)), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(_:)), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
}
func textFieldDidBeginEditing(_ textField: UITextField) {
activeField = textField
}
func textFieldDidEndEditing(_ textField: UITextField) {
activeField = nil
}
Keyboard Notifications
func keyboardWillHide(_ notification: Notification) {
var info = notification.userInfo!
let keyboardSize = info[UIKeyboardFrameEndUserInfoKey] as? CGRect
let contentInsets: UIEdgeInsets = UIEdgeInsetsMake(0.0, 0.0, -keyboardSize!.height , 0.0)
scrollView.contentInset = contentInsets
}
func keyboardWillShow(_ notification: Notification) {
var info = notification.userInfo!
let keyboardSize = info[UIKeyboardFrameEndUserInfoKey] as? CGRect
let contentInsets: UIEdgeInsets = UIEdgeInsetsMake(0.0, 0.0, keyboardSize!.height + 8, 0.0)
scrollView.contentInset = contentInsets
}
It works as magic.
But I'm facing the following issue. When keyboard hides after text editing, the view goes back to its previous condition, which should be like this, but the scrolling becomes disabled. I can't figure out why.. I set the scrollView.isScrollEnabled = true, but it doesn't solve my problem.
How can I get rid of this?
This line is wrong (well, the whole thing is wrong, but this is the particular line that you are complaining about):
let contentInsets: UIEdgeInsets = UIEdgeInsetsMake(0.0, 0.0, -keyboardSize!.height, 0.0)
Better would be
let contentInsets: UIEdgeInsets = .zero
Even better, when the keyboard shows, save the content insets. When it hides, restore them to the value you saved.
Related
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
}
}
I am trying to write a code to ensure UITextField move up when keyboard present. I have written a code based on what I found in stack overflow however it is not working. The original code was written in objective-c but I am not familiar with the it and hence decided to to write code in swift.
can anyone tell what I may be doing wring here?
{
// moving text box up when tyoing
func registerForKeyboardNotifications()
{
//Adding notifies on keyboard appearing
NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWasShown(notification:)), name: NSNotification.Name.UIKeyboardDidShow, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillBeHidden(notification:)), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
}
#objc func keyboardWasShown(notification: NSNotification)
{
//Need to calculate keyboard exact size due to Apple suggestions
self.scrollView.isScrollEnabled = true
let info : NSDictionary = notification.userInfo! as NSDictionary
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 (yourEmail) != nil
{
if (!aRect.contains(yourEmail!.frame.origin))
{
self.scrollView.scrollRectToVisible(yourEmail!.frame, animated: true)
}
}
}
#objc func keyboardWillBeHidden(notification: NSNotification)
{
//Once keyboard disappears, restore original positions
let info : NSDictionary = notification.userInfo! as NSDictionary as NSDictionary
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)
{
yourEmail = textField
}
func textFieldDidEndEditing(_ textField: UITextField)
{
yourEmail = nil
}
}
Try this code
func textFieldDidBeginEditing(textField: UITextField!)
{
self.scrollView.setContentOffset(CGPoint.init(x: 0, y: scrollBy), animated: true)
// scrollBy - pass the height you want your scrollview to be scrolled when keyboard appears
}
func textFieldDidEndEditing(textField: UITextField!)
{
self.scrollView.setContentOffset(CGPoint.init(x: 0, y: 0), animated: true)
self.view.endEditing(true);
}
below is the code I wrote with the help of suggestions:
{
func textFieldDidBeginEditing(_ textField: UITextField) {
if textField.frame.maxY > self.view.frame.height * 0.6
{
self.scrollView.setContentOffset(CGPoint.init(x: 0, y: textField.frame.maxY - self.view.frame.height * 0.6 + 2.0), animated: true)
}
else{
return
}
}
func textFieldDidEndEditing(_ textField: UITextField)
{
self.scrollView.setContentOffset(CGPoint.init(x: 0, y: 0), animated: true)
self.view.endEditing(true);
}
}
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.
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)
}
}
}
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?