My app structure currently is a navigation controller that leads to viewcontrollers with scrollview embedded in them. The textfields, textview, buttons etc are on top of the scrollview. By using my current code, the scrollview moves the view when UITextFields are clicked, but not UITextViews. I have tried to adopt Apple's recommended method and tweaked it for UITextViews.
Also, in the function keyboardWillBeShown, the part that checks if active textfield/textview in hidden by the keyboard and scrolls, seems to not make any difference at all.
Where have I gone wrong? Thanks
class unwellBasic: UIViewController, UITextViewDelegate,
UIScrollViewDelegate, UITextFieldDelegate {
#IBOutlet var scrollView: UIScrollView!
weak var activeTextView : UITextView?
weak var activeTextField : UITextField?
#IBOutlet var main: UITextView!
#IBOutlet var initials: UITextField!
#IBOutlet var maleButton: UIButton!
#IBOutlet var femaleButton: UIButton!
#IBOutlet var age: UITextField!
#IBOutlet var test: UITextField!
func registerForKeyboardNotifications() {
let notificationCenter = NSNotificationCenter.defaultCenter()
notificationCenter.addObserver(self,
selector: "keyboardWillBeShown:",
name: UIKeyboardWillShowNotification,
object: nil)
notificationCenter.addObserver(self,
selector: "keyboardWillBeHidden:",
name: UIKeyboardWillHideNotification,
object: nil)
}
#IBAction func back(sender: AnyObject) {
self.navigationController?.popViewControllerAnimated(true)
}
#IBAction func next(sender: AnyObject) {
self.performSegueWithIdentifier("bodySegue", sender: self)
}
func tapped() {
initials.resignFirstResponder()
main.resignFirstResponder()
age.resignFirstResponder()
test.resignFirstResponder()
self.activeTextView = nil
}
// Called when the UIKeyboardDidShowNotification is sent.
func keyboardWillBeShown(sender: NSNotification) {
let info: NSDictionary = sender.userInfo!
let value: NSValue = info.valueForKey(UIKeyboardFrameEndUserInfoKey) as! NSValue
let keyboardSize: CGSize = value.CGRectValue().size
let contentInsets: UIEdgeInsets = UIEdgeInsetsMake(0.0, 0.0, keyboardSize.height, 0.0)
scrollView.contentInset = contentInsets
scrollView.scrollIndicatorInsets = contentInsets
// If active text field is hidden by keyboard, scroll it so it's visible
if self.activeTextView != nil {
var aRect: CGRect = self.view.frame
aRect.size.height -= keyboardSize.height
let activeTextViewRect: CGRect? = self.activeTextView!.frame
let activeTextViewOrigin: CGPoint? = activeTextViewRect?.origin
if (!CGRectContainsPoint(aRect, activeTextViewOrigin!)) {
scrollView.scrollRectToVisible(activeTextViewRect!, animated:true)
}
}
if self.activeTextField != nil {
var aRect: CGRect = self.view.frame
aRect.size.height -= keyboardSize.height
let activeTextViewRect: CGRect? = self.activeTextField!.frame
let activeTextViewOrigin: CGPoint? = activeTextViewRect?.origin
if (!CGRectContainsPoint(aRect, activeTextViewOrigin!)) {
scrollView.scrollRectToVisible(activeTextViewRect!, animated:true)
}
}
}
// Called when the UIKeyboardWillHideNotification is sent
func keyboardWillBeHidden(sender: NSNotification) {
let contentInsets: UIEdgeInsets = UIEdgeInsetsMake(0.0, 0.0, 0.0, 0.0)
scrollView.contentInset = contentInsets
self.scrollView .setContentOffset(CGPointMake(0, 0), animated: true)
self .viewDidLayoutSubviews()
self.activeTextView = nil
}
func textViewDidBeginEditing(textView: UITextView) {
self.activeTextView = textView
scrollView.scrollEnabled = true
}
func textViewDidEndEditing(textView: UITextView) {
self.activeTextView = nil
scrollView.scrollEnabled = false
self.scrollView .setContentOffset(CGPointMake(0, 0), animated: true)
self .viewDidLayoutSubviews()
}
func textFieldDidBeginEditing(textField: UITextField) {
activeTextField = textField
scrollView.scrollEnabled = true
}
func textFieldDidEndEditing(textField: UITextField) {
activeTextField = nil
scrollView.scrollEnabled = false
}
func textFieldShouldReturn(textField: UITextField) -> Bool // called when 'return' key pressed. return NO to ignore.
{
textField.resignFirstResponder()
return true;
}
override func viewDidLoad() {
super.viewDidLoad()
var tap = UITapGestureRecognizer (target: self, action: ("tapped"))
self.view.addGestureRecognizer(tap)
self.main.delegate = self
self.initials.delegate = self
self.age.delegate = self
self.registerForKeyboardNotifications()
}
inherit your scroll view with TPKeyboardAvoiding, you don't need to code too much
Related
I am using scrolview for view with height 1000, initially i don't want my scrolView to scroll. if i tap on any textField then i want my scrolview to scroll and if i return keyboard then i don't want my scrollview to scroll.
Here i am able to textfield up when when keyboard appears but i am unable to return textfield to its orginal position when i return keyboard and when i return keyboard i dont want my view to scroll,
Please help me in the code.
class ViewController: UIViewController, UITextFieldDelegate {
#IBOutlet weak var scrolView: UIScrollView!
#IBOutlet weak var upTFLD: UITextField!
var activeTextField = UITextField()
#IBOutlet weak var downTFLD: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
upTFLD.delegate = self
downTFLD.delegate = self
NotificationCenter.default.addObserver(self, selector: #selector(onKeyboardAppear(_:)), name: UIResponder.keyboardDidShowNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(onKeyboardDisappear(_:)), name: UIResponder.keyboardDidHideNotification, object: nil)
}
#objc func onKeyboardAppear(_ notification: NSNotification) {
let info = notification.userInfo!
let rect: CGRect = info[UIResponder.keyboardFrameBeginUserInfoKey] as! CGRect
let kbSize = rect.size
let insets = UIEdgeInsets(top: 0, left: 0, bottom: kbSize.height+20, right: 0)
self.scrolView.contentInset = insets
self.scrolView.scrollIndicatorInsets = insets
var visibleRect: CGRect = self.scrolView.convert(self.scrolView.bounds, to: self.view)
visibleRect.size.height -= rect.size.height;
let inputRect: CGRect = self.activeTextField.convert(self.activeTextField.bounds, to: self.scrolView)
if (visibleRect.contains(inputRect)) {
self.scrolView.scrollRectToVisible(inputRect, animated: true)
}
}
#objc func onKeyboardDisappear(_ notification: NSNotification) {
self.scrolView.contentInset = UIEdgeInsets.zero
self.scrolView.scrollIndicatorInsets = UIEdgeInsets.zero
}
public func textFieldDidBeginEditing(_ textField: UITextField) {
activeTextField = textField
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
activeTextField.resignFirstResponder()
return true
}
}
initially i dont want scrolling, and if i return keyboard i need textfield come to its original position and no scrolling again.
Only keyboard appears then only i need scrolling. please help me in the code.
You just need to set the contentOffset of your ScrollView right after the keyboard is hidden.
Create a variable to store offsetBeforeShowKeyboard
var offsetBeforeShowKeyboard: CGFloat?
When view is initially loaded:
self.scrollView.isScrollEnabled = false
When select any TextField:
public func textFieldDidBeginEditing(_ textField: UITextField) {
self.scrollView.isScrollEnabled = true
if (self.offsetBeforeShowKeyboard == nil) {
self.offsetBeforeShowKeyboard = self.scrollView.contentOffset
}
}
When keyboard is hidden
#objc func onKeyboardDisappear(_ notification: NSNotification) {
self.scrollView.isScrollEnabled = false
if let offset = self.offsetBeforeShowKeyboard {
self.scrolView.setContentOffset(offset, animated: true)
}
self.offsetBeforeShowKeyboard = nil
}
In viewWillAppear
yourScrollview.isScrollEnabled = false
After Keyboard appears make it true
yourScrollview.isScrollEnabled = true
Alternatively you can use IQKeyboard manager to take care of textfields.Checkout: IQKeyboardManager
After trying out most of the solutions posted here, I'm still having trouble moving the textfield to show up above keyboard in a scrollview.
These are links I followed from Stackoverflow solutions:
Link 1
Link 2
Link 3
I'm working on a signup screen that has 1 field behind the keyboard when it shows up.
Here's my code:
class SignUpViewController: UIViewController, UITextFieldDelegate, UIScrollViewDelegate, UIPopoverPresentationControllerDelegate {
#IBOutlet var firstNameTextField: UITextField!
#IBOutlet var lastNameTextField: UITextField!
#IBOutlet var phoneNumberTextField: UITextField!
#IBOutlet var emailTextField: UITextField!
#IBOutlet var submitButton: UIButton!
#IBOutlet var professionButton: UIButton!
var scrollView: UIScrollView?
var activeTextField:UITextField? = UITextField()
override func viewDidLoad() {
super.viewDidLoad()
let notificationCenter = NSNotificationCenter.defaultCenter()
notificationCenter.addObserver(self, selector: #selector(SignUpViewController.keyboardWasShown(_:)), name: UIKeyboardWillShowNotification, object: nil)
notificationCenter.addObserver(self, selector: #selector(SignUpViewController.keyboardWillBeHidden(_:)), name: UIKeyboardWillHideNotification, object: nil)
scrollView = UIScrollView(frame: CGRect(x: 0, y: 0, width: self.view.frame.size.width, height: self.view.frame.size.height))
scrollView!.contentSize = CGSizeMake(self.view.frame.width, self.view.frame.height)
defaultSettings()
}
func defaultSettings() {
self.firstNameTextField.delegate = self
self.lastNameTextField.delegate = self
self.emailTextField.delegate = self
self.phoneNumberTextField.delegate = self
}
func deregisterFromKeyboardNotifications()
{
//Removing notifies on keyboard appearing
NSNotificationCenter.defaultCenter().removeObserver(self, name: UIKeyboardWillShowNotification, object: nil)
NSNotificationCenter.defaultCenter().removeObserver(self, name: UIKeyboardWillHideNotification, object: nil)
}
func keyboardWasShown(notification: NSNotification)
{
//Need to calculate keyboard exact size due to Apple suggestions
//self.scrollView!.scrollEnabled = true
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 (!CGRectContainsPoint(aRect, activeTextField!.frame.origin))
{
// print(activeTextField?.frame)
// var scrollPoint = CGPointMake(0.0, activeTextField!.frame.origin.y - (keyboardSize!.height-15))
self.scrollView!.scrollRectToVisible((activeTextField?.frame)!, animated: true)
//self.scrollView?.setContentOffset(scrollPoint, animated: true)
}
}
func keyboardWillBeHidden(notification: NSNotification)
{
//Once keyboard disappears, restore original positions
//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)
// self.scrollView!.scrollEnabled = false
}
func textFieldDidBeginEditing(textField: UITextField)
{
activeTextField = textField
}
func textFieldDidEndEditing(textField: UITextField)
{
activeTextField = nil
}
As you can see, I've tried scrollRectToVisible with frame and setContentOffset with Point. Both didn't work. Whereas the code picks on the emailTextField right as the hidden textfield.
I was also struggling with the same issue as you did, I don't know if you were successful and found solution, but I finally used setContentOffset function instead of scrollRectToVisible and it worked.
Swift 3.x example:
if (!aRect.contains(activeTextView!.frame.origin)) {
self.scrollView.setContentOffset(CGPoint(x:0, y:self.activeTextView!.frame.origin.y), animated: true)
}
SWIFT 4 Try this
scrollView.scrollRectToVisible(myElementView.frame, animated: true)
where myElementView can be any element
You should also set the scrollView delegate as:
self.scrollView.delegate = self as! UIScrollViewDelegate
ScrollView delegate methods work with this.
I have UIView which is container for button "send" and textView.
When I starting editing I have keyboard on the screen and I change Y origin of the container to move it higher and not be covered by keyboard.
When I finish editing I got keyboard hidden and my container(UIView with button and textView) just disappears! But if I turn simulator right or left this became visible again.
Here is the animation code that move my container up and down:
func textViewDidChange(textView: UITextView) {
self.messagePromptLabel.hidden = messageTextView.hasText() ? true : false
}
func textViewDidEndEditing(textView: UITextView) {
if !self.messageTextView.hasText() {
self.messagePromptLabel.hidden = false
}
}
func didTapScrollView(){
self.view.endEditing(true)
}
func keyboardWasShown(notification: NSNotification) {
let dict: NSDictionary = notification.userInfo!
let keyboardSize: NSValue = dict.valueForKey(UIKeyboardFrameEndUserInfoKey) as! NSValue
let frameKeyboardSize: CGRect = keyboardSize.CGRectValue()
UIView.animateWithDuration(0.3, animations: { () -> Void in
self.chatScrollView.frame.origin.y -= frameKeyboardSize.height
self.messageView.frame.origin.y -= frameKeyboardSize.height
}) { (finished: Bool) -> Void in
}
}
func keyboardWillHide(notification: NSNotification) {
UIView.animateWithDuration(0.3, animations: { () -> Void in
self.chatScrollView.frame.origin.y = self.chatScrollViewOriginY!
self.messageView.frame.origin.y = self.messageTextViewOriginY!
}) { (finished: Bool) -> Void in
}
}
Some additional code
This code may help find out something.
#IBOutlet weak var messageTextView: UITextView!
#IBOutlet weak var chatScrollView: UIScrollView!
#IBOutlet weak var messagePromptLabel: UILabel!
#IBOutlet weak var messageView: UIView!
var chatScrollViewOriginY: CGFloat?
var messageTextViewOriginY: CGFloat?
var messageArray = [String]()
var senderArray = [String]()
var currentUserImage: UIImage?
var recipientImage: UIImage?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
messageTextView.addSubview(messagePromptLabel)
self.title = recipientNickname
chatScrollViewOriginY = self.chatScrollView.frame.origin.y
messageTextViewOriginY = self.messageTextView.frame.origin.y
print(" messageTextViewOriginY \(messageTextViewOriginY)")
NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardWasShown:", name: UIKeyboardDidShowNotification, object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardWillHide:", name: UIKeyboardWillHideNotification, object: nil)
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: "didTapScrollView")
tapGestureRecognizer.numberOfTouchesRequired = 1
chatScrollView.addGestureRecognizer(tapGestureRecognizer)
chatScrollView.backgroundColor = UIColor.redColor()
}
func textViewDidChange(textView: UITextView) {
self.messagePromptLabel.hidden = messageTextView.hasText() ? true : false
}
func textViewDidEndEditing(textView: UITextView) {
if !self.messageTextView.hasText() {
self.messagePromptLabel.hidden = false
}
}
func didTapScrollView(){
self.view.endEditing(true)
}
Question:
What make my UIView container invisible all the time when I hidding keyboard and make it visible when I turn my phone left or right?
You need to add this code in your send button action:
self.view.endEditing(true)
I'm looking for a solution to my problem but all the suggestions I've found are not working.
I started with a working solution that is based on two TextField with decimal pad. To dismiss the keyboard when is tapped anywhere, considering i do not have the return key, I used successfully:
view.endEditing(true)
My problem stars when I decide to add UIScrollView in order to move up the UITextField on the bottom of the screen.
The view.endEditing(true) is not working because the scrollview is covering view. I add a TapGestureRecognizer and everything work except the keyboard dismiss.
Under the TapGestureReconizer function if I use resignFirstResponder on the activeTextFiled the app crash going to AppDelegate with error:
Thread 1: signal SIGABRT.
class ViewController: UIViewController, UITextFieldDelegate {
var activeField: UITextField?
#IBOutlet weak var textFieldTop: UITextField!
#IBOutlet weak var textFieldBottom: UITextField!
#IBOutlet weak var scrollView: UIScrollView!
func textFieldDidBeginEditing(textField: UITextField) {
activeField = textField
}
func textFieldDidEndEditing(textField: UITextField) {
activeField = nil
}
func registerForKeyboardNotifications() {
NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardWasShown:", name: UIKeyboardWillShowNotification, object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardWillBeHidden", name: UIKeyboardWillHideNotification, object: nil)
}
func deregisterFromKeyBoardNotification() {
NSNotificationCenter.defaultCenter().removeObserver(self, name: UIKeyboardWillShowNotification, object: nil)
NSNotificationCenter.defaultCenter().removeObserver(self, name: UIKeyboardWillHideNotification, object: nil)
}
func keyboardWasShown(notification: NSNotification) {
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 + 20, 0.0)
self.scrollView.contentInset = contentInsets
self.scrollView.scrollIndicatorInsets = contentInsets
var aRect : CGRect = self.view.frame
aRect.size.height -= keyboardSize!.height
if let _ = activeField
{
if (!CGRectContainsPoint(aRect, activeField!.frame.origin))
{
self.scrollView.scrollRectToVisible(activeField!.frame, animated: true)
}
}
self.scrollView.scrollEnabled = false
}
func keyboardWillBeHidden(notification: NSNotification)
{
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.scrollView.scrollEnabled = false
}
override func viewDidLoad() {
super.viewDidLoad()
registerForKeyboardNotifications()
self.textFieldBottom.delegate = self
self.textFieldTop.delegate = self
scrollView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: "closeKeyboard"))
}
func closeKeyboard() {
activeField?.resignFirstResponder()
}
}
I've tried different solution but none of them are working.
I was still looking to some delegate or other possible missing part in the code/storyboard but spending additional hours checking I found the problem.
NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardWillBeHidden:", name: UIKeyboardWillHideNotification, object: nil)
I forgot in the original code the ":" at the end of "keyboardWillBeHidden:".
This was throwing an exception because this function has an argument.
Now everything is working properly
I have tried to follow other guides on here but to no avail.
My app structure currently is a navigation controller that leads to viewcontrollers with scrollview embedded in them. The textfields, textview, buttons etc are on top of the scrollview. When the keyboard appears, and I dismiss it by tapping outside, the scrollview moves up with the keyboard, but does not come down. It seems like this problem is brought on by having the Navigation controller or the nav bar. How can I fix it?
EDIT: I just realized that every time I simulate that particular viewcontroller, everything in it is moved down the same distance before I tap on any textField/textView. Then when the keyboard is shown, it simply moves it up so it looks like how it's setup in storyboard. How do I get rid of the initial downward displacement?
#IBOutlet var scrollView: UIScrollView!
var activeTextView:UIView? = UIView()
#IBOutlet var main: UITextView!
#IBOutlet var initials: UITextField!
#IBOutlet var maleWord: UITextField!
#IBOutlet var maleButton: UIButton!
#IBOutlet var femaleWord: UITextField!
#IBOutlet var femaleButton: UIButton!
#IBOutlet var age: UITextField!
func registerForKeyboardNotifications() {
let notificationCenter = NSNotificationCenter.defaultCenter()
notificationCenter.addObserver(self,
selector: "keyboardWillBeShown:",
name: UIKeyboardWillShowNotification,
object: nil)
notificationCenter.addObserver(self,
selector: "keyboardWillBeHidden:",
name: UIKeyboardWillHideNotification,
object: nil)
}
func tapped() {
initials.resignFirstResponder()
main.resignFirstResponder()
age.resignFirstResponder()
}
// Called when the UIKeyboardDidShowNotification is sent.
func keyboardWillBeShown(sender: NSNotification) {
let info: NSDictionary = sender.userInfo!
let value: NSValue = info.valueForKey(UIKeyboardFrameEndUserInfoKey) as! NSValue
let keyboardSize: CGSize = value.CGRectValue().size
let contentInsets: UIEdgeInsets = UIEdgeInsetsMake(0.0, 0.0, keyboardSize.height, 0.0)
scrollView.contentInset = contentInsets
scrollView.scrollIndicatorInsets = contentInsets
// If active text field is hidden by keyboard, scroll it so it's visible
var aRect: CGRect = self.view.frame
aRect.size.height -= keyboardSize.height
let activeTextViewRect: CGRect? = self.main.frame
let activeTextViewOrigin: CGPoint? = activeTextViewRect?.origin
if (!CGRectContainsPoint(aRect, activeTextViewOrigin!)) {
scrollView.scrollRectToVisible(activeTextViewRect!, animated:true)
}
}
// Called when the UIKeyboardWillHideNotification is sent
func keyboardWillBeHidden(sender: NSNotification) {
self.scrollView .setContentOffset(CGPointMake(0, 0), animated: true)
self .viewDidLayoutSubviews()
self.activeTextView = nil
}
func textViewDidBeginEditing(textView: UITextView) {
self.activeTextView = textView
scrollView.scrollEnabled = true
}
func textViewDidEndEditing(textView: UITextView) {
self.activeTextView = nil
scrollView.scrollEnabled = false
self.scrollView .setContentOffset(CGPointMake(0, 0), animated: true)
}
func textFieldShouldReturn(textField: UITextField) -> Bool // called when 'return' key pressed. return NO to ignore.
{
textField.resignFirstResponder()
return true;
}
override func viewDidLoad() {
super.viewDidLoad()
var tap = UITapGestureRecognizer (target: self, action: ("tapped"))
self.view.addGestureRecognizer(tap)
self.main.delegate = self
self.initials.delegate = self
self.age.delegate = self
self.registerForKeyboardNotifications()
}
Update your function like following
func keyboardWillBeHidden(sender: NSNotification) {
let contentInsets: UIEdgeInsets = UIEdgeInsetsMake(0.0, 0.0, 0.0, 0.0)
scrollView.contentInset = contentInsets
self.scrollView .setContentOffset(CGPointMake(0, 0), animated: true)
self .viewDidLayoutSubviews()
self.activeTextView = nil
}