Cannot move Keyboard up when UITextView is clicked - ios

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
}

Related

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

Move UITextField up when keyboard appears

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);
}
}

On selecting a textfield, adjust view height for keyboard

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)
}
}
}

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?

ScrollView and keyboard in Swift

I started creating a simple iOS app that does some operations.
But I'm having some problems when the keyboard appears, hiding one of my textfields.
I think it's a common problem and I did some research but I couldn't find anything that solved my problem.
I want to use a ScrollView rather than animate the textfield to make it visible.
In ViewDidLoad, register the notifications:
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow), name:UIResponder.keyboardWillShowNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide), name:UIResponder.keyboardWillHideNotification, object: nil)
Add below observer methods which does the automatic scrolling when keyboard appears.
#objc func keyboardWillShow(notification:NSNotification) {
guard let userInfo = notification.userInfo else { return }
var keyboardFrame:CGRect = (userInfo[UIResponder.keyboardFrameBeginUserInfoKey] as! NSValue).cgRectValue
keyboardFrame = self.view.convert(keyboardFrame, from: nil)
var contentInset:UIEdgeInsets = self.scrollView.contentInset
contentInset.bottom = keyboardFrame.size.height + 20
scrollView.contentInset = contentInset
}
#objc func keyboardWillHide(notification:NSNotification) {
let contentInset:UIEdgeInsets = UIEdgeInsets.zero
scrollView.contentInset = contentInset
}
The top answer for swift 3:
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)
And then:
func keyboardWillShow(notification:NSNotification){
//give room at the bottom of the scroll view, so it doesn't cover up anything the user needs to tap
var userInfo = notification.userInfo!
var keyboardFrame:CGRect = (userInfo[UIKeyboardFrameBeginUserInfoKey] as! NSValue).cgRectValue
keyboardFrame = self.view.convert(keyboardFrame, from: nil)
var contentInset:UIEdgeInsets = self.theScrollView.contentInset
contentInset.bottom = keyboardFrame.size.height
theScrollView.contentInset = contentInset
}
func keyboardWillHide(notification:NSNotification){
let contentInset:UIEdgeInsets = UIEdgeInsets.zero
theScrollView.contentInset = contentInset
}
Here is a complete solution, utilizing guard and concise code. Plus correct code in keyboardWillHide to only reset the bottom to 0.
#IBOutlet private weak var scrollView: UIScrollView!
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
registerNotifications()
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
scrollView.contentInset.bottom = 0
}
private func registerNotifications() {
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow), name: UIResponder.keyboardWillShowNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide), name: UIResponder.keyboardWillHideNotification, object: nil)
}
#objc private func keyboardWillShow(notification: NSNotification){
guard let keyboardFrame = notification.userInfo![UIResponder.keyboardFrameEndUserInfoKey] as? NSValue else { return }
scrollView.contentInset.bottom = view.convert(keyboardFrame.cgRectValue, from: nil).size.height
}
#objc private func keyboardWillHide(notification: NSNotification){
scrollView.contentInset.bottom = 0
}
for Swift 4.0
In ViewDidLoad
// setup keyboard event
NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillShow), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillHide), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
Add below observer methods which does the automatic scrolling when keyboard appears.
#objc func keyboardWillShow(notification:NSNotification){
var userInfo = notification.userInfo!
var keyboardFrame:CGRect = (userInfo[UIKeyboardFrameBeginUserInfoKey] as! NSValue).cgRectValue
keyboardFrame = self.view.convert(keyboardFrame, from: nil)
var contentInset:UIEdgeInsets = self.ui_scrollView.contentInset
contentInset.bottom = keyboardFrame.size.height
ui_scrollView.contentInset = contentInset
}
#objc func keyboardWillHide(notification:NSNotification){
let contentInset:UIEdgeInsets = UIEdgeInsets.zero
ui_scrollView.contentInset = contentInset
}
contentInset doesn't work for me, because I want the scrollview move all the way up above the keyboard. So I use contentOffset:
func keyboardWillShow(notification:NSNotification) {
guard let keyboardFrameValue = notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue else {
return
}
let keyboardFrame = view.convert(keyboardFrameValue.cgRectValue, from: nil)
scrollView.contentOffset = CGPoint(x:0, y:keyboardFrame.size.height)
}
func keyboardWillHide(notification:NSNotification) {
scrollView.contentOffset = .zero
}
Swift 5 Only adjust ScrollView when TextField is hidden by keyboard (for multiple TextFields)
Add / Remove Observers:
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow), name: UIResponder.keyboardWillChangeFrameNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide), name: UIResponder.keyboardWillHideNotification, object: nil)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
NotificationCenter.default.removeObserver(self)
}
Keep track of these values so you can return to your original position:
var scrollOffset : CGFloat = 0
var distance : CGFloat = 0
Adjust ScrollView contentOffset depending on TextField Location:
#objc func keyboardWillShow(notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue {
var safeArea = self.view.frame
safeArea.size.height += scrollView.contentOffset.y
safeArea.size.height -= keyboardSize.height + (UIScreen.main.bounds.height*0.04) // Adjust buffer to your liking
// determine which UIView was selected and if it is covered by keyboard
let activeField: UIView? = [textFieldA, textViewB, textFieldC].first { $0.isFirstResponder }
if let activeField = activeField {
if safeArea.contains(CGPoint(x: 0, y: activeField.frame.maxY)) {
print("No need to Scroll")
return
} else {
distance = activeField.frame.maxY - safeArea.size.height
scrollOffset = scrollView.contentOffset.y
self.scrollView.setContentOffset(CGPoint(x: 0, y: scrollOffset + distance), animated: true)
}
}
// prevent scrolling while typing
scrollView.isScrollEnabled = false
}
}
#objc func keyboardWillHide(notification: NSNotification) {
if distance == 0 {
return
}
// return to origin scrollOffset
self.scrollView.setContentOffset(CGPoint(x: 0, y: scrollOffset), animated: true)
scrollOffset = 0
distance = 0
scrollView.isScrollEnabled = true
}
Make sure to use [UIResponder.keyboardFrameEndUserInfoKey] to get the proper keyboard height the first time.
From the answer by Sudheer Palchuri, converted for Swift 4.
In ViewDidLoad, register the notifications:
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)
And then:
// MARK: - Keyboard Delegates
func textFieldShouldReturn(textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}
#objc func keyboardWillShow(notification:NSNotification){
var userInfo = notification.userInfo!
var keyboardFrame:CGRect = (userInfo[UIKeyboardFrameBeginUserInfoKey] as! NSValue).cgRectValue
keyboardFrame = self.view.convert(keyboardFrame, from: nil)
var contentInset:UIEdgeInsets = self.scrollView.contentInset
contentInset.bottom = keyboardFrame.size.height
self.scrollView.contentInset = contentInset
}
#objc func keyboardWillHide(notification:NSNotification){
let contentInset:UIEdgeInsets = UIEdgeInsets.zero
self.scrollView.contentInset = contentInset
}
Reading the links you sent to me, I found a way to make it work, thanks!:
func textFieldDidBeginEditing(textField: UITextField) {
if (textField == //your_field) {
scrollView.setContentOffset(CGPointMake(0, field_extra.center.y-280), animated: true)
callAnimation()
viewDidLayoutSubviews()
}
}
func textFieldDidEndEditing(textField: UITextField) {
if (textField == //your_field){
scrollView .setContentOffset(CGPointMake(0, 0), animated: true)
viewDidLayoutSubviews()
}
}
This is a refinement on Zachary Probst solution posted above.
I ran into a few issues with his solution and fixed it and enhanced it a bit.
This version does not need to pass in a list of UITextView controls. It finds the first responder in the current view. It also handles UITextView controls at any level in the View hierarchy.
I think his safeArea calculation wasn't quite right scrollView.contentOffset.y needed sign changed. It didn't show up if it was not scrolled. This fixed incremental scrolling. It might have been from other changes I made.
This works if the user jumps around to other UITextViews while the keyboard is up.
This is a Base Class I use for a bunch of ViewControllers. The inherited ViewController just needs to set the UIScrollViewer which activates this code behavior.
class ThemeAwareViewController: UIViewController
{
var scrollViewForKeyBoard: UIScrollView? = nil
var saveOffsetForKeyBoard: CGPoint?
func findViewThatIsFirstResponder(view: UIView) -> UIView?
{
if view.isFirstResponder {
return view
}
for subView in view.subviews {
if let hit = findViewThatIsFirstResponder(view: subView) {
return hit
}
}
return nil
}
#objc func keyboardWillShow(notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue {
if let scrollView = scrollViewForKeyBoard {
var safeArea = self.view.frame
safeArea.size.height -= scrollView.contentOffset.y
safeArea.size.height -= keyboardSize.height
safeArea.size.height -= view.safeAreaInsets.bottom
let activeField: UIView? = findViewThatIsFirstResponder(view: view)
if let activeField = activeField {
// This line had me stumped for a while (I was passing in .frame)
let activeFrameInView = view.convert(activeField.bounds, from: activeField)
let distance = activeFrameInView.maxY - safeArea.size.height
if saveOffsetForKeyBoard == nil {
saveOffsetForKeyBoard = scrollView.contentOffset
}
scrollView.setContentOffset(CGPoint(x: 0, y: distance), animated: true)
}
}
}
}
#objc func keyboardWillHide(notification: NSNotification) {
guard let restoreOffset = saveOffsetForKeyBoard else {
return
}
if let scrollView = scrollViewForKeyBoard {
scrollView.setContentOffset(restoreOffset, animated: true)
self.saveOffsetForKeyBoard = nil
}
}
}
You can animate your scrollview to center on your UITextField on keyboard appearance (ie. making your textfield the first responder) via a scroll offset. Here are a couple of good resources to get you started (there are a bunch on this site):
How programmatically move a UIScrollView to focus in a control above keyboard?
How to make a UIScrollView auto scroll when a UITextField becomes a first responder
Additionally, if you simply use a UITableView with your content in cells, when the textfield becomes first responder, the UITableViewController will automatically scroll to the textfield cell for you (though I'm not sure this is what you want to do).
An answer for Swift 3, based on the one proposed by Daniel Jones, but safer (thanks to the guard), more concise and with consistent scroll indicator insets:
#objc private func keyboardWillBeShown(notification: NSNotification) {
guard let keyboardFrameValue = notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue else { return }
let keyboardFrame = view.convert(keyboardFrameValue.cgRectValue, from: nil)
scrollView.contentInset.bottom = keyboardFrame.size.height
scrollView.scrollIndicatorInsets = scrollView.contentInset
}
#objc private func keyboardWillBeHidden() {
scrollView.contentInset = .zero
scrollView.scrollIndicatorInsets = scrollView.contentInset
}
In Swift4, just add the following extension.
extension UIViewController {
func setupViewResizerOnKeyboardShown() {
NotificationCenter.default.addObserver(self,
selector: #selector(self.keyboardWillShowForResizing),
name:
Notification.Name.UIKeyboardWillShow,
object: nil)
NotificationCenter.default.addObserver(self,
selector: #selector(self.keyboardWillHideForResizing),
name: Notification.Name.UIKeyboardWillHide,
object: nil)
}
#objc func keyboardWillShowForResizing(notification: Notification) {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue,
let window = self.view.window?.frame {
// We're not just minusing the kb height from the view height because
// the view could already have been resized for the keyboard before
self.view.frame = CGRect(x: self.view.frame.origin.x,
y: self.view.frame.origin.y,
width: self.view.frame.width,
height: window.origin.y + window.height - keyboardSize.height)
} else {
debugPrint("We're showing the keyboard and either the keyboard size or window is nil: panic widely.")
}
}
#objc func keyboardWillHideForResizing(notification: Notification) {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue {
let viewHeight = self.view.frame.height
self.view.frame = CGRect(x: self.view.frame.origin.x,
y: self.view.frame.origin.y,
width: self.view.frame.width,
height: viewHeight + keyboardSize.height)
} else {
debugPrint("We're about to hide the keyboard and the keyboard size is nil. Now is the rapture.")
}
}
}
There is a simple solution here
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
NotificationCenter.default.addObserver(self, selector: #selector(ViewController.keyboardWillShow), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(ViewController.keyboardWillHide), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
}
func keyboardWillShow(_ notification:Notification) {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
tableView.contentInset = UIEdgeInsetsMake(0, 0, keyboardSize.height, 0)
}
}
func keyboardWillHide(_ notification:Notification) {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
tableView.contentInset = UIEdgeInsetsMake(0, 0, 0, 0)
}
}
In case anyone is looking for Objective-C code for this solution:
- (void)keyboardWasShown:(NSNotification *)notification {
NSDictionary* info = [notification userInfo];
CGSize kbSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
UIEdgeInsets contentInsets = baseScrollView.contentInset;
contentInsets.bottom = kbSize.height;
baseScrollView.contentInset = contentInsets;
}
- (void)keyboardWillBeHidden:(NSNotification *)notification {
UIEdgeInsets contentInsets = UIEdgeInsetsZero;
baseScrollView.contentInset = contentInsets;
[baseScrollView endEditing:YES];
}
In my situation it was
ScrollView
-->
TableView
-->
TableViewCell
So I had to get y position in relative to keyboard frame and check if keyboard y position and my active field y position was intersecting or not
#objc func keyboardWillShow(_ notification: Foundation.Notification) {
var userInfo = notification.userInfo!
var keyboardFrame:CGRect = (userInfo[UIResponder.keyboardFrameEndUserInfoKey] as! NSValue).cgRectValue
keyboardFrame = self.view.convert(keyboardFrame, from: nil)
var contentInset:UIEdgeInsets = self.scrollView!.contentInset
contentInset.bottom = keyboardFrame.size.height
let loc = self.activeTextField?.convert(activeTextField!.bounds, to: self.view)
if keyboardFrame.origin.y < loc!.origin.y {
self.scrollView?.contentOffset = CGPoint.init(x: (self.scrollView?.contentOffset.x)!, y: loc!.origin.y)
}
if self.scrollView?.contentInset.bottom == 0 {
self.scrollView?.contentInset = contentInset
}
}

Resources