scrollView problem with UITextView when keyboard hide/show notification - swift

i have multiple textField and textView in scrollView in my viewController. i handle keyboard show and hide with these codes:
i added these line of code in viewDidLoad:
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow), name:UIResponder.keyboardWillChangeFrameNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide), name:UIResponder.keyboardWillHideNotification, object: nil)
and also these 2 function:
#objc func keyboardWillShow(notification:NSNotification){
guard let keyboardValue = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue else { return }
let keyboardScreenEndFrame = keyboardValue.cgRectValue
let keyboardViewEndFrame = view.convert(keyboardScreenEndFrame, from: view.window)
let bottom = keyboardViewEndFrame.height - view.safeAreaInsets.bottom + 16
self.scrollView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: bottom , right: 0)
}
#objc func keyboardWillHide(notification:NSNotification){
self.scrollView.contentInset = UIEdgeInsets.zero
}
everything is ok when i start to edit a textField. but it does not work with textView and has problem to scroll to active textView.
how can i fix it?

The reason of this issue is explained here, so if you want to use UITextView inside a UIScrollView then uncheck the Scrolling Enabled from right menu inspector or set it False from the code.

Related

How to show and hide the keyboard with a subview

I have a custom UIView that is a subview on a UIViewController.
I have it added in my storyboard and set it to Hidden.
My subview is also within another UIView that I'm using as a 'blur view' which is also initially Hidden.
I have functions that will unhide & hide the subviews.
My custom subview has a UITextField. I can show the keyboard and move the subview up with no problems. When I type in the keyboard or dismiss it my subview moves up and to the left. When I try to show my subview again it shows incorrectly (up and to the left).
The custom subview starts at the center of my screen.
The goal is move it up when the keyboard shows so it will not cover the subview or the UITextField, allow the user to type in the UITextField, and then dismiss the keyboard and move the custom subview back to the center.
In my UIViewController:
// Showing the custom sub view
func displayCustomSubView() {
if let window = UIApplication.shared.keyWindow {
self.blurView.isHidden = false
self.customSubView.isHidden = false
self.blurView.frame = window.frame
self.customSubView.center = window.center
window.addSubview(self.blurView)
UIApplication.shared.keyWindow?.bringSubviewToFront(self.blurView)
}
}
// Hiding the custom sub view
// the custom sub view has a button I tap to hide
#objc func dismissCustomSubView() {
self.blurView.isHidden = true
self.customSubView.isHidden = true
}
// Show Keyboard
// Since I am using the window to make sure my blur view expands to the full frame, I have tried just moving the window up
#objc func keyboardWillShow(sender: NSNotification) {
if let window = UIApplication.shared.keyWindow {
window.frame.origin.y = -75
}
}
// Hide Keyboard
#objc func keyboardWillHide(sender: NSNotification) {
if let window = UIApplication.shared.keyWindow {
window.frame.origin.y = 0
}
}
// Custom Subview Extension
extension CustomSubView: UITextFieldDelegate {
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}
}
Added the Custom Subview Extension above.
First add this notification within your viewDidLoad(). And make a global variable called var keyboardH: CGFloat = 0:
NotificationCenter.default.addObserver(
self,
selector: #selector(keyboardWillShow),
name: UIResponder.keyboardWillShowNotification,
object: nil
)
And this function below:
#objc func keyboardWillShow(_ notification: Notification) {
if let keyboardFrame: NSValue = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue {
let keyboardRectangle = keyboardFrame.cgRectValue
let keyboardHeight = keyboardRectangle.height
self.keyboardH = keyboardHeight
}
This function is called every time the keyboard is present and will reveal the keyboard height and later we can use this variable.
So in your code:
#objc func keyboardWillShow(sender: NSNotification) {
if let window = UIApplication.shared.keyWindow {
let position = window.frame.origin.y - keyboardH
window.frame.origin.y = position
}
}

Expand uitableview cell height while typing and avoid keyboard

I have a tableview with textview. I want to set tableviewcell height according to the content of uitextview while typing and also if keyboard hides uitexview scroll table.
Below is the code for expanding cell height according to the content
func updateCellHeight(indexPath: NSIndexPath, comment: String,textview: UITextView) {
let tempDict = arrayTexts[indexPath.row] as! NSMutableDictionary
tempDict.setValue(comment, forKey: "content")
arrayTexts[indexPath.row] = tempDict
self.tblFields.beginUpdates()
self.tblFields.endUpdates()
}
The above code is working but its not avoiding keyboard
Below code is for avoiding keyboard
func textviewBeginEditing(textview: UITextView) {
let contentInsets = UIEdgeInsets(top: 0, left: 0, bottom: keyboardHeight, right: 0)
self.tblFields.contentInset = contentInsets
self.tblFields.scrollIndicatorInsets = contentInsets
// If active text field is hidden by keyboard, scroll it so it's visible
// Your app might not need or want this behavior.
var aRect = self.view.frame
aRect.size.height -= keyboardHeight
if !aRect.contains(textview.frame.origin) {
self.tblFields.scrollRectToVisible(textview.frame, animated: false)
}
}
The above code sometimes avoids keyboard and sometimes not but the tableview continuously scrolls up on typing and is not working properly. Whats my mistake?
Remove the code from textviewBeginEditing function and add following code
func updateCellHeight(indexPath: NSIndexPath, comment: String, textview: UITextView) {
let tempDict = arrayTexts[indexPath.row] as! NSMutableDictionary
tempDict.setValue(comment, forKey: "value")
arrayTexts[indexPath.row] = tempDict
if textview.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize).height != textview.frame.size.height {
UIView.setAnimationsEnabled(false)
self.tblFields.beginUpdates()
self.tblFields.endUpdates()
UIView.setAnimationsEnabled(true)
}
}

Keyboard covers text view while typing in tableview cell

I have a text view inside my tableview cell and I use
self.tableView.beginUpdates()
self.tableView.endUpdates()
to update my cell height based on the amount of texts that user types and everything works pretty well except for the keyboard cover my text view area if my text view goes below the keyboard. I have tried
NSNotification.Name.UIKeyboardWillShow
to push my table view up and think it is the solution but the problem still exist and it really doesn't help at all in this case. Is there a way to keep the text always above the keyboard while typing? Thank you all for helping me and below is how I update my table view cell height
//set textView delegate in custom cell and use textView didchange func
func textViewDidChange(_ textView: UITextView) {
textDidChanges?()
}
//then in my cellForRowAt
cell.textDidChanges = {
self.tableView.beginUpdates()
self.tableView.endUpdates()
}
Inside the method to execute when a UIResponder.keyboardWillShowNotification is invoked,
get the keyboard's size and add it to the tableView and its scroll Indicator Insets.
NotificationCenter.default.addObserver(self, selector: #selector(VC.keyboardWillShow), name: UIResponder.keyboardWillShowNotification, object: nil)
func keyboardWillShow(_ notification: Notification) {
guard let info = notification.userInfo,
let keyboardFrameRect = info[UIResponder.keyboardFrameBeginUserInfoKey] as? NSValue
else { return }
let keyboardRect = keyboardFrameRect.cgRectValue
let contentInset = UIEdgeInsets(top: 0, left: 0, bottom: keyboardRect.height, right: 0)
tableView.contentInset = contentInset
tableView.scrollIndicatorInsets = contentInset
}

table views only scrolls to textfields bottom edge

The bottommost cell of my table view is a cell with a textField. When the user taps it, I want to scroll it so that the cell is right above the keyboard.
When I call the scrollRectToVisible(...) with animated false everything works as expected, but when animated is set to true the table scrolls the cell only so far, that the bottom of the textField is right above the keyboard (See left picture). Yet the bottonInsets should be correct, since I can scroll the cell the last bit manually and the cell sits right how it should (See right picture).
I think the table view scrolling the textField's bottom edge above the keyboard is the default behavior of a table view, but I'm afraid I don't know why it seems to override my own scrolling when I want it animated.
Left picture:
The textFields bottom edge right above the keyboard (I kept the border style so you can see it better).
Right picture:
How I want it. Cell's bottom edge right above the keyboard.
func repositionTextfieldCell(in tableView: UITableView) {
guard let textFieldCell = tableView.bottommostCell() else { return }
guard let keyboardRect = activeKeyboardRect else { return }
// - Adjust insets
var bottomInset = keyboardRect.size.height
tableView.contentInset.bottom = bottomInset
tableView.scrollIndicatorInsets.bottom = bottomInset
// - Make cell visible
let x = textFieldCell.frame.minX
let y = textFieldCell.frame.maxY
tableView.scrollRectToVisible(CGRect(origin: CGPoint(x: x, y: y),
size: CGSize(width: 1, height: 1)), animated: true)
}
add this in viewDidLoad() and create a NSlayout constraint for tableview bottom.
NotificationCenter.default.addObserver(
self,
selector: #selector(keyboardWillShow),
name: NSNotification.Name.UIKeyboardWillShow,
object: nil
)
create the function
#objc func keyboardWillShow(_ notification: Notification) {
if let keyboardFrame: NSValue = notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue {
let keyboardRectangle = keyboardFrame.cgRectValue
let keyboardHeight = keyboardRectangle.height
tableBottomConstraint.constant = self.view.frame.height - keyboardHeight
}
}
repeat the process to reset the tableBottomConstraint.constant = 0 in keyboardWillHide() method.
I could fix the problem.
The behavior seems to be depended on were scrollRectToVisible(...) is called. The behavior I described in the question occurs when scrollRectToVisible(...) is called in keyboardDidShow(...).
However when you call scrollRectToVisible(...) in keyboardWillShow(...) and set animated to false the cell / rect is pushed up by the keyboard sliding in. Which I think looks great.

Swift Message Text Area

I am creating a new app and I want to add message area like:
When user click the text area keyboard will shown and user start writing message right button will be send button and text area with will be grow like:
What is the easy way to do it?
I hava a idea to do it but I think it is not the the best way. I am thinking to solve it programmaticly. In viewLoad I will create button and text area programmaticly then when user starts writing I will create them again.
As explained in the official apple documentation you can work with UITextField delegate methods to intercept the differents states.
For example you can use textFieldDidBeginEditing(_:) to handle the moment when the user start to edit. Here , you can handle constraints to enlarge your textField (pay attention, you must working with constraints if you use autolayout, dont make frame modifications):
func textFieldDidBeginEditing(textField: UITextField!) {
//
// hide photo button
// change mic button to send button
self.textFieldTrailingConstraint.constant = 10.0
UIView.animateWithDuration(0.5) {
self.view.layoutIfNeeded()
}
}
After that you can handle the other delegates:
func textFieldShouldEndEditing(textField: UITextField!) -> Bool {
return false
}
func textFieldShouldReturn(textField: UITextField!) -> Bool {
textField.resignFirstResponder()
return true
}
where you can repeat your animation or do whatever you want.
I'd also recommend to solve this in code. Loading the view you'll have to create all buttons and if the user starts writing you should recreate some of the buttons. But keep in Mind that you have to ensure, that the Textfield is the same instance or that you have to resign the responder to it by code.
In addition to textfield delegates, you need to handle keyboard as well:
override func viewDidLoad() {
super.viewDidLoad()
NSNotificationCenter.defaultCenter().addObserver(self, selector:"keyboardWillAppear:", name: UIKeyboardWillShowNotification, object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector:"keyboardWillDisappear:", name: UIKeyboardWillHideNotification, object: nil)}
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(true)
NSNotificationCenter.defaultCenter().removeObserver(self)}
func keyboardWillAppear(notification: NSNotification){
var userInfo:NSDictionary = notification.userInfo!
var keyboardSize:CGSize = userInfo.objectForKey(UIKeyboardFrameBeginUserInfoKey)!.CGRectValue().size
var contentInsets:UIEdgeInsets = UIEdgeInsetsMake(0, 0, 0, 0)
self.tableView.contentInset = contentInsets
self.tableView.scrollIndicatorInsets = contentInsets
var messageFrame:CGRect = self.commentField.frame
messageFrame.origin.y -= keyboardSize.height
self.commentField.frame = messageFrame}
func keyboardWillDisappear(notification: NSNotification){
var userInfo:NSDictionary = notification.userInfo!
var keyboardSize:CGSize = userInfo.objectForKey(UIKeyboardFrameBeginUserInfoKey)!.CGRectValue().size
UIView.beginAnimations(nil, context: nil)
UIView.setAnimationDuration(0.25)
self.tableView.contentInset = UIEdgeInsetsZero
UIView.commitAnimations()
self.tableView.scrollIndicatorInsets = UIEdgeInsetsZero
var messageFrame:CGRect = self.commentField.frame
messageFrame.origin.y += keyboardSize.height
self.commentField.frame = messageFrame}