Keyboard covers text view while typing in tableview cell - swift

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
}

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

how to save the action of a button press inside of a cell, so that I can add an image without it duplicating or disappearing

I have a tableViewController that has three functions, two of which are working successfully.
Add cells by pressing button inside of toolbar = works
Being able to insert text into UITextView inside of cell, without the text duplicating and moving around = works
When button in cell is pressed, checkMarkImage appears, without getting duplicated when scrolling and moving around = does not work
The checkMarkImage does not stay put at the cell in which the button is pressed. It reappears when scrolling and sometimes disappears, despite my efforts of using a Boolean value, in order to track the cells of which have been checkmarked.
the configuration of the button inside of the my customCellClass:
#IBAction func checkMarkButton(_ sender: UIButton) {
if checkedOff{
UIImageView.animate(withDuration: 0.3, delay: 0, options: [], animations: {
self.checkMarkImage.alpha = 0.0
self.notesTextView.isEditable = true
self.notesTextView.textColor = .white
self.checkedOff = false
},completion: nil)
}
else{
UIImageView.animate(withDuration: 0.3, delay: 0, options: [], animations: {
self.checkMarkImage.alpha = 1.0
self.notesTextView.isEditable = false
self.notesTextView.textColor = .orange
self.checkedOff = true
}, completion: nil)
}
}
The handling of cell inside cellForRowAt:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cellIdentifier = "TableViewNotesCell"
let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as! TableViewNotesCell
cell.notesTextView.text = cellNumber[indexPath.row]
if cellNumber[indexPath.row] < " "{
print("celltext is less than nothing")
cell.notesTextView.textColor = .white
cell.notesTextView.isEditable = true
cell.checkMarkImage.alpha = 0.0
}
else{
if cell.checkedOff{
print("cell has been checked off")
cell.notesTextView.textColor = .orange
cell.notesTextView.isEditable = false
cell.checkMarkImage.alpha = 1.0
}
}
My expect the cell's checkMarkImage to stay at the cell in which the button is pressed, but the actual effect is that the checkMarkImage is re-occurring, when scrolling, and sometimes completely disappears
I am afraid that by doing this you not gonna achieve the desired outcome.
you must persist your data when you using UITableView or UICollectionView because these reuse the Cell when scrolling. so when you scroll the UITableView you are getting duplicate image or sometime loosing it. what you can do is :
Use an array of dictionary as dataSource to persist your data for the TableView.
you can also create your own model and use as the dataSource of TableView.
and if your data is large enough then you can go for CoreData / Sqllight.

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

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.

Custom expanding/collapsing Date Picker expands off view when it's in the bottom cell of table view - Swift

I have a custom inline date picker that expands and collapses just like the Apple Calendar events date pickers. The problem is that I need the Date Picker to in a UITableViewCell that is at the bottom of the UITableView, but when it expands, you cannot see the Picker without scrolling the view down manually. For a similar issue (UITextField disappears behind keyboard when it's being edited if it's below the height of the keyboard) I was able to fix it with an animation in the beginEditing and endEditing delegate functions:
let keyboardHeight: CGFloat = 216
UIView.animateWithDuration(0.25, delay: 0.25, options: UIViewAnimationOptions.CurveEaseInOut, animations: {
self.view.frame = CGRectMake(0, (self.view.frame.origin.y - keyboardHeight), self.view.bounds.width, self.view.bounds.height)
}, completion: nil)
Does anyone know of a similar animation that would work for maybe placing in didSelectRowAtIndexPath, and if it's a date picker the screen will adjust by scrolling down to show the full date picker expanded, and then it will adjust back when the row is not selected anymore - or something along these lines?
I'm not sure if the fact that there's already an animation occurring to expand/collapse the date picker would conflict with this or if it's just a matter of precedence for the animations to occur. Anyways I will post some code below:
When the DatePicker is selected in the tableView:
/**
Used to notify the DatePickerCell that it was selected. The DatePickerCell will then run its selection animation and expand or collapse.
*/
public func selectedInTableView() {
expanded = !expanded
UIView.transitionWithView(rightLabel, duration: 0.25, options:UIViewAnimationOptions.TransitionCrossDissolve, animations: { () -> Void in
self.rightLabel.textColor = self.expanded ? self.tintColor : self.rightLabelTextColor
}, completion: nil)
}
didSelectRowAtIndexPath function:
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
// Deselect automatically if the cell is a DatePickerCell.
let cell = self.tableView(tableView, cellForRowAtIndexPath: indexPath)
if (cell.isKindOfClass(DatePickerCell)) {
let datePickerTableViewCell = cell as! DatePickerCell
datePickerTableViewCell.selectedInTableView()
tableView.deselectRowAtIndexPath(indexPath, animated: true)
} else if(cell.isKindOfClass(PickerCell)) {
let pickerTableViewCell = cell as! PickerCell
pickerTableViewCell.selectedInTableView()
tableView.deselectRowAtIndexPath(indexPath, animated: true)
}
tableView.beginUpdates()
tableView.endUpdates()
}
I think I came up with a solution that seems to work as needed, I wrote this code in the didSelectRowAtIndexPath delegate function after the call to selectedInTablView():
if(datePickerTableViewCell.isExpanded() && datePickerTableViewCell.datePicker == billDatePicker.datePicker) {
tableView.contentOffset = CGPoint(x: 0, y: self.view.frame.height + datePickerTableViewCell.datePickerHeight())
}