How to set textview height to space left when keyboard appears (Swift) - swift

I have been searching for ages for a way to set the height of my textview to the leftover space after the keyboard appears. The keyboard never hides and is up permanently.
How am I able to place the textview in the space at the top of the screen when the keyboard is up.
I am desperate for an answer as nothing I have found has worked, and I also need it in Swift, not Objective-C as I think one of the reasons I haven't been able to get it working is because I have been translating the code wrong.
Thanks!

Finally found a solution.
func keyboardWillShow(notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue() {
let contentInsets = UIEdgeInsets(top: 0, left: 0, bottom: keyboardSize.height, right: 0)
textView.contentInset = contentInsets
}
}

This is how you find the height of the onscreen keyboard:
onscreen keyboard height stackoverflow
next I assume your textView is covering the whole view of parent viewController.
make your viewController conform to UITextViewDelegate and implement this method ->
func textViewDidBeginEditing(textView: UITextView) -> Bool{
textView.bounds.size.height = view.bounds.size.height - KEYBOARDHEIGHT(method from that link of stackoverflow)
}
also in the viewDidLoad :
yourTextView.delegate = self

Related

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.

UITextView Height Adjustment

I'm trying to create a ViewController which will hold some large text. I used UILabel but since the text can be long, it won't work. Trying to switch to UITextViewbut I'm having some problems.
I'm using ScrollView since there is a image on the top and button on the bottom. So I don't it scrolling inside the UITextView itself. I've disabled it.
I want to Text to height itself automatically according to the text length. So it can scroll with the images, buttons etc. Just like the apps Instapaper, Medium, Pocket etc.
I've tried all the code and solutions on StackOverflow, but they either didn't work or they were Objective-C.
Storyboard Structure:
View Controller -> View -> ScrollView -> Image, TextView, Button
class ViewController: UIViewController {
#IBOutlet weak var textView: UITextView!
var theContent = fromClass.text
override func viewDidLoad() {
super.viewDidLoad()
textView.text = theContent
}
}
There is few ways to change the size of the UITextView
extension String {
func bounds(approximated width: CGFloat, approximated height: CGFloat, font: UIFont) -> CGRect {
let size = CGSize(width: width, height: height)
let attribs = [NSAttributedStringKey.font: font]
return NSString(string: self).boundingRect(with: size, options: .usesLineFragmentOrigin, attributes: attribs, context: nil)
}
}
approximated width = width of your UITextView I assume it is going to be static value which you won't change.
approximated height = minimal height of your UITextView.
The method going to return frame of your text input. Take from it height property and depending of how big is that change the size of the UITextView.
If it won't satisfy you, you can use another way.
func heightOfString(_ attributes: [NSAttributedKeyString : Any]) -> CGFloat {
let size = self.size(withAttributes: attributes)
return size.height
}
The same usage as from above.
Adjust your view hierarchy to the following view hierarchy :
View Controller -> View -> ScrollView -> View1 -> Image, UILabel, Button
1- give view1 top-left-bottom-right constraints and and align it center X to it's parent view
2- add UIImageView in the top of View1 and give it top-left-right constraint and a fixed height constraint.
3- add a UILabel and give it top constraint to the image and right-bottom-left constraint to it's superview.
make UILabel number of lines = 0
The content not should be scrollable.
Note that: in your case; you do not need UITextView .
If you want to use UITextView:
1- add a height constraint to the UITextView and connect an outlet to it
called: constTextViewHeight
let textViewContentHeight = textView.contentSize.height//call this line after adding the text to the textView
self.constTextViewHeight.contant = textViewContentHeight
self.view.layoutIfNeeded()

UI Elements Moving When Character Typed In Text Field

When a user selects the bottom text field all elements in the view move up by the height of the keyboard until the user dismisses the keyboard.
The problem occurs after the user selects the bottom text field. Initially the elements all move up correctly. However, when the first character is typed by the user all of the elements move back down to the original position. As a result, the user can no longer see the text field they are editing. Once the user dismisses the keyboard they see the elements go up where they should have been, and then come right back down to the original position.
I have narrowed down what is causing the issue, but I do not understand why it is happening. My project can be found on GitHub at https://github.com/JMNolan/memesicle
I am still very new to Swift and fairly new to coding in general, so all help is really appreciated. I have also included the code snippets below that are relevant to this process for anyone who does not want to use GitHub
This is what moves the elements up the height of the keyboard
//moves the view up when the keyboard appears to keep the text field
visible
#objc func keyboardWillShow (notification: NSNotification){
if bottomTextFieldActive == true{
keyboardHeight = getKeyboardHeight(notification:
notification)
imagePickerView.frame.origin.y -= keyboardHeight
topText.frame.origin.y -= keyboardHeight
bottomText.frame.origin.y -= keyboardHeight
toolbar.frame.origin.y -= keyboardHeight
}
}
This is what moves the elements back down the height of the keyboard when the keyboard is dismissed
//moves the view down when the keyboard is dismissed to show the full view again
#objc func keyboardWillHide (notification: NSNotification){
imagePickerView.frame.origin.y += keyboardHeight
topText.frame.origin.y += keyboardHeight
bottomText.frame.origin.y += keyboardHeight
toolbar.frame.origin.y += keyboardHeight
//shareButton.frame.origin.y += keyboardHeight
print("keyboard just hid")
}
This is what calls the function above
func subscribeToKeyboardNotifications(){
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)
}
This is how I get the keyboardHeight used when moving the elements up and down the height of the keyboard.
//get the height of the keyboard to determine how far up to move the view when editing the bottom text field
func getKeyboardHeight(notification: NSNotification) -> CGFloat {
let userinfo = notification.userInfo
let keyboardSize = userinfo![UIKeyboardFrameEndUserInfoKey] as!NSValue
return keyboardSize.cgRectValue.height
}
The reason your screen elements are jumping around when you type in the UITextField is because of your view constraints. When you add constraints to your view, you kind of forfeit the ability to later move the subviews around by altering their frames - each time the view recalculates the location of its subviews, it will go back to using the original values as provided by the constraints and ignore the frame changes you made.
Since you've got a few different screen elements you want to move, the easiest way to solve this is to put all your subviews (text field, image view, etc) inside a UIScrollView and then resize the scroll view's content inset property when the keyboard comes up. This would completely replace your code that changes the frame sizes. Apple has a help document about how to do this, but the important parts are below:
Adjusting your content typically involves temporarily resizing one or more views and positioning them so that the text object remains visible. The simplest way to manage text objects with the keyboard is to embed them inside a UIScrollView object or one of its subclasses, like UITableView. Note that UITableViewController automatically resizes and repositions its table view when there is inline editing of text fields (to learn more, see View Controllers and Navigation-Based Apps).
When the keyboard is displayed, all you have to do is reset the content area of the scroll view and scroll the desired text object into position. Thus, in response to a UIKeyboardDidShowNotification, your handler method would do the following:
Get the size of the keyboard.
Adjust the bottom content inset of your scroll view by the keyboard height.
Scroll the target text field into view.
// Called when the UIKeyboardDidShowNotification is sent.
- (void)keyboardWasShown:(NSNotification*)aNotification
{
NSDictionary* info = [aNotification userInfo];
CGSize kbSize = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size;
UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, kbSize.height, 0.0);
scrollView.contentInset = contentInsets;
scrollView.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.
CGRect aRect = self.view.frame;
aRect.size.height -= kbSize.height;
if (!CGRectContainsPoint(aRect, activeField.frame.origin) ) {
[self.scrollView scrollRectToVisible:activeField.frame animated:YES];
}
}
// Called when the UIKeyboardWillHideNotification is sent
- (void)keyboardWillBeHidden:(NSNotification*)aNotification
{
UIEdgeInsets contentInsets = UIEdgeInsetsZero;
scrollView.contentInset = contentInsets;
scrollView.scrollIndicatorInsets = contentInsets;
}
I faced the same issue, and after read shocking's answer, I found a solution. Thank shocking
I overrode updateViewConstraints() method, and inside, I updated constraint for TextView bottom to View bottom, with constant = keyboard height.
override func updateViewConstraints() {
writeCommentView.autoPinEdge(toSuperviewEdge: .bottom, withInset: keyboardHeight) //I use PureLayout library
super.view.updateConstraints()
}
And then, I call updateViewConstraints() in keyboardWillShow(), keyboardWillHide()
#objc func keyboardWillShow(notification: NSNotification) {
//Code for move up textview here
self.updateViewConstraints()
}