TextView capitalizes first two characters instead of one - swift

So I've added placeholder functionality to UITextView, and now it capitalizes the first two characters of input text instead of one as it's supposed to.
I would appreciate you guys' help in solving this problem if you can help in any way. I've been trying, but I seem to be getting nowhere.
Here's the code:
class ViewController: UIViewController {
#IBOutlet weak var textView: UITextView!
override func viewDidLoad() {
super.viewDidLoad()
textView.delegate = self
textView.text = "Type something"
textView.textColor = UIColor.rgb(197, 197, 199)
}
}
extension ViewController: UITextViewDelegate {
// Put cursor at the beginning of TextView when editing begins
func textViewDidBeginEditing(_ textView: UITextView) {
textView.selectedTextRange = textView.textRange(from: textView.beginningOfDocument, to: textView.beginningOfDocument)
}
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
// Sets placeholder for UITextView
let updatedText = (textView.text as NSString).replacingCharacters(in: range, with: text)
if updatedText.isEmpty {
textView.text = "Type something"
textView.textColor = UIColor.rgb(197, 197, 199)
textView.selectedTextRange = textView.textRange(from: textView.beginningOfDocument, to: textView.beginningOfDocument)
} else if textView.textColor == UIColor.rgb(197, 197, 199) && !text.isEmpty {
textView.textColor = UIColor.black
textView.text = text
} else {
return true
}
return false
}
// Prevents selecting the placeholder
func textViewDidChangeSelection(_ textView: UITextView) {
if self.view.window != nil {
if textView.textColor == UIColor.rgb(197, 197, 199) {
textView.selectedTextRange = textView.textRange(from: textView.beginningOfDocument, to: textView.beginningOfDocument)
}
}
}
}
Thank you!

The code below solves capitalization of the first 2 letters in text view.
func textViewDidChange(_ textView: UITextView) {
if textView.text.isEmpty {
textView.text = "Type something"
textView.textColor = self.placeholderTextColor
} else {
textView.textColor = .black
}
}
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
if text.isEmpty {
let updatedText = (textView.text as NSString).replacingCharacters(in: range, with: text)
if updatedText.isEmpty {
textView.text = "Type something"
textView.textColor = self.placeholderTextColor
textView.selectedRange = NSRange(location: 0, length: 0)
}
} else {
if textView.text == "Type something" {
textView.text = ""
}
textView.textColor = .black
}
return true
}
The reason why the text view capitalized first 2 characters in text view is that you've returned false in below delegate method
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool

Related

UITextView Right Alignment in swift

i am trying text enter in textview right to left alignment but at that time textview starting position enter space not taking textview in swift
#IBOutlet weak var textview: UITextView!
#IBOutlet weak var sampleTF: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
textview.textAlignment = .right
textview.isScrollEnabled = true
textview.heightAnchor.constraint(equalToConstant: 52.0).isActive = true
textview.delegate = self
}
#IBAction func sampleButton(_ sender: Any) {
}
func textViewDidChange(_ textView: UITextView) {
let size = CGSize(width: textView.frame.width, height: 200)
let estimateSize = textView.sizeThatFits(size)
guard textView.contentSize.height < 100.0 else { textview.isScrollEnabled = true; return}
textview.isScrollEnabled = false
textview.constraints.forEach { (constriant) in
if constriant.firstAttribute == .height {
constriant.constant = estimateSize.height
}
}
}
As seen on other posts it has to do with how spaces are handled (). You can add the following delegate code to replace spaces with non breaking spaces :
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText string: String) -> Bool {
if (textView == self.textView) {
let oldString = textView.text!
let newStart = oldString.index(oldString.startIndex, offsetBy: range.location)
let newEnd = oldString.index(oldString.startIndex, offsetBy: range.location + range.length)
let newString = oldString.replacingCharacters(in: newStart..<newEnd, with: string)
textView.text = newString.replacingOccurrences(of: " ", with: "\u{00a0}")
return false;
} else {
return true;
}
}

How to trigger code anytime a UItextfield is empty

I would like to have code for anytime a UItextfield is empty. Not just on the viewdidload but all the time. I tried putting something like if textField.isEmpty == true in the editing changed action although the issue I was having is if you type more than 5 characters and then hold down backspace the code doesn't get triggered. Any ideas for what to do?
Hi you need to subscribe on editing changed 
How to check if the field is empty?
let textField = UITextField()
textField.addTarget(self, action: #selector(textChanged), for: .editingChanged)
#objc func textChanged () {
if textField.text == "" || textField.text == nil {
print("IS EMPTY")
} else {
print("NON EMPTY")
}
}
How to set max length to UITextField
class ViewController: UIViewController, UITextFieldDelegate {
override func viewDidLoad() {
let textField = UITextField()
textField.delegate = self
}
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
guard let textFieldText = textField.text,
let rangeOfTextToReplace = Range(range, in: textFieldText) else {
return false
}
let substringToReplace = textFieldText[rangeOfTextToReplace]
let count = textFieldText.count - substringToReplace.count + string.count
return count <= 5
}
}

How to hide the keyboard when user press return button?

I have a UITextView in my ViewController, When user press on that textView, the keyboard shows up. This is not the problem. My problem is: How to hide the keyboard when user press return button in the keyboard? I have tried some functions but apparently they worked only with UITextFields.
import UIKit
class NewNoteVC: UIViewController, UITextViewDelegate {
#IBOutlet weak var newText: UITextView!
override func viewDidLoad() {
super.viewDidLoad()
newText.text = "Start typing"
newText.textColor = UIColor.lightGray
newText.tintColor = UIColor(red: 251/255, green: 140/255, blue: 139/255, alpha: 1)
}
/// setting placeholdere
func textViewDidBeginEditing(_ textView: UITextView) {
if newText.textColor == UIColor.lightGray {
newText.text = nil
newText.textColor = UIColor.black
}
}
func textViewDidEndEditing(_ textView: UITextView) {
if newText.text.isEmpty {
newText.text = "Placeholder"
newText.textColor = UIColor.lightGray
}
}
/// limit characters
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
let currentText = newText.text ?? ""
guard let stringRange = Range(range, in: currentText) else { return false }
let changedText = currentText.replacingCharacters(in: stringRange, with: text)
return changedText.count <= 1000
}
/// hide keyboard when user touch outside screen
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
self.view.endEditing(true)
}
/// hide keyboard when user press return button
#IBAction func backToMainPage(_ sender: Any) {
dismiss(animated: true, completion: nil)
}
}
Whatever the reason might be to dismiss keyboard on return of a UITextView the simplest solution would be:
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
if text == "\n" {
textView.resignFirstResponder()
}
{...}
}

Keep on adding dynamic height text view in scroll view on button click

I am preparing a form in which we have to add a UI Text View on every button click. This text view must increase its height as text is typed and scroll view should increase its content size accordingly.
I have tried using UITableView for this with scrolling enabled = false and placing text view inside cell with scrolling enabled = false. Now I am trying to increase content size for table view as well as scroll view with the addition of new cell on every button click but unable to do so. Can anyone help with this ?
var notesArray = [String]()
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if notesArray.count == 0{
return 1
}else{
return notesArray.count
}
}
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell : AddAssetCell = tableView.dequeueReusableCell(withIdentifier: "AddAssetCell", for: indexPath) as! AddAssetCell
cell.contentView.isUserInteractionEnabled = true
cell.txtView.delegate = self
if notesArray.count > 0{
cell.txtView.text = notesArray[indexPath.row]
}
return cell
}
func textViewDidChange(_ textView: UITextView) {
let startHeight = textView.frame.size.height
let calcHeight = textView.sizeThatFits(textView.frame.size).height
if startHeight != calcHeight {
UIView.setAnimationsEnabled(false) // Disable animations
self.tableViewNotes.beginUpdates()
self.tableViewNotes.endUpdates()
let scrollTo = self.tableViewNotes.contentSize.height - self.tableViewNotes.frame.size.height
self.tableViewNotes.setContentOffset(CGPoint(x : 0, y : scrollTo), animated: false)
contraintHeightTblViewNotes.constant = self.tableViewNotes.contentSize.height
self.scrollViewAddAsset.contentSize.height = self.scrollViewAddAsset.contentSize.height + scrollTo
UIView.setAnimationsEnabled(true) // Re-enable animations.
}
}
//MARK: - Text View Delegates
func textViewDidBeginEditing(_ textView: UITextView) {
if textView.textColor == UIColor.lightGray {
textView.text = nil
textView.textColor = UIColor.black
}
}
func textViewDidEndEditing(_ textView: UITextView) {
if textView.text.isEmpty {
textView.text = "Notes"
textView.textColor = UIColor.lightGray
}else{
if notesArray.count > 0 {
notesArray.insert(textView.text, at: notesArray.count - 1)
}else{
notesArray.append(textView.text)
}
}
}
#IBAction func btnAddMoreAction(_ sender: Any)
{
notesArray.append("")
tableViewNotes.reloadData()
}
Ok let's talk a little bit about Self-Sizing Cell in UITableView, if you haven't known it, here is a clear tutorial https://www.raywenderlich.com/129059/self-sizing-table-view-cells.
It let your UITableView grown it cell height automatically in summarization. So the next challenge is UITextViewDelegate, here is my favorite solution when facing Self-Sizing UITextView.:
func textViewDidBeginEditing(_ textView: UITextView) {
if textView === txtDescription, textView.text == txtDescripttionPlaceHolder {
textView.text = nil
textView.selectedRange = NSMakeRange(0, 0)
}
}
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
let newLenght = textView.text.count + text.count - range.length
if textView === txtDescription, textView.text == txtDescripttionPlaceHolder, newLenght > 0 {
if text.count == 0 {
return false
}
textView.text = nil
}
return true
}
func textViewDidChange(_ textView: UITextView) {
if textView === txtDescription {
resizeTxtDescription()
}
}
func textViewDidEndEditing(_ textView: UITextView) {
if textView == txtDescription {
textView.text.trimNewlinesAndWhitespaces()
textView.text = textView.text.count > 0 ? textView.text : txtDescripttionPlaceHolder
resizeTxtDescription()
}
}
private func resizeTxtDescription() {
let fixedWidth = self.txtDescription.frame.size.width
self.txtDescription.sizeThatFits(CGSize(width: fixedWidth, height: CGFloat.greatestFiniteMagnitude))
let newSize = txtDescription.sizeThatFits(CGSize(width: fixedWidth, height: CGFloat.greatestFiniteMagnitude))
UIView.animate(withDuration: 0.5) { [weak self] in
self?.txtDescriptionHeightConstraint.constant = newSize.height
self?.view.setNeedsLayout()
self?.view.layoutIfNeeded()
}
}
After setting up your UITableViewCell, declaring a constraint keep your UITextView Height like txtDescriptionHeightConstraint in my situation, so you can change your UITextView height via changing the constraint's constant.

dismiss keyboard with a uiTextView

I am sure this is not that difficult, but I am having trouble finding info on how to dismiss a keyboard with the return/done key using a textview, not a textfield. here is what I have tried so far(which works with a textfield.)
Thanks very much in advance for any help!
// PostTravelQuestion.swift
class PostTravelQuestion: UIViewController, UITextViewDelegate {
#IBAction func closepostpage(sender: AnyObject) {
dismissViewControllerAnimated(true, completion: nil)
}
#IBOutlet var postquestion: UITextView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
postquestion.delegate = self
}
self addDoneToolBarToKeyboard:self.textView
/*func textViewShouldEndEditing(textView: UITextView) -> Bool {
textView.resignFirstResponder()
return true
}*/
/*override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
postquestion.resignFirstResponder()
self.view.endEditing(true)
}*/
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func textViewShouldReturn(textView: UITextView!) -> Bool {
self.view.endEditing(true);
return true;
}
}
This works for me:
import UIKit
class ViewController: UIViewController, UITextViewDelegate {
#IBOutlet weak var textView: UITextView!
override func viewDidLoad() {
super.viewDidLoad()
textView.delegate = self
}
/* Updated for Swift 4 */
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
if(text == "\n") {
textView.resignFirstResponder()
return false
}
return true
}
/* Older versions of Swift */
func textView(textView: UITextView, shouldChangeTextInRange range: NSRange, replacementText text: String) -> Bool {
if(text == "\n") {
textView.resignFirstResponder()
return false
}
return true
}
}
Add UITextViewDelegate to your class and then set your delegate for your textView or your textField in viewDidLoad. Should look something like this:
// in viewDidLoad
textField.delegate = self
textView.delegate = self
Swift 3
// hides text views
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
if (text == "\n") {
textView.resignFirstResponder()
return false
}
return true
}
// hides text fields
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if (string == "\n") {
textField.resignFirstResponder()
return false
}
return true
}
Swift 2.0
The below syntax has been tested for Swift 1.2 & Swift 2.0
func textView(textView: UITextView, shouldChangeTextInRange range: NSRange, replacementText text: String) -> Bool {
if(text == "\n") {
textView.resignFirstResponder()
return false
}
return true
}
Below code will dismissing the keyboard when click return/done key on UITextView.
In Swift 3.0
import UIKit
class ViewController: UIViewController, UITextViewDelegate {
#IBOutlet var textView: UITextView!
override func viewDidLoad() {
super.viewDidLoad()
textView.delegate = self
}
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool
{
if(text == "\n")
{
view.endEditing(true)
return false
}
else
{
return true
}
}
In Swift 2.2
func textView(textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool
{
if text == "\n"
{
view.endEditing(true)
return false
}
else
{
return true
}
}
Easiest and best way to do this using UITextView Extension.
Credit: http://www.swiftdevcenter.com/uitextview-dismiss-keyboard-swift/
Your ViewController Class
class ViewController: UIViewController {
#IBOutlet weak var myTextView: UITextView!
override func viewDidLoad() {
super.viewDidLoad()
// 1
self.myTextView.addDoneButton(title: "Done", target: self, selector: #selector(tapDone(sender:)))
}
// 2
#objc func tapDone(sender: Any) {
self.view.endEditing(true)
}
}
Add UITextView Extension
extension UITextView {
func addDoneButton(title: String, target: Any, selector: Selector) {
let toolBar = UIToolbar(frame: CGRect(x: 0.0,
y: 0.0,
width: UIScreen.main.bounds.size.width,
height: 44.0))//1
let flexible = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)//2
let barButton = UIBarButtonItem(title: title, style: .plain, target: target, action: selector)//3
toolBar.setItems([flexible, barButton], animated: false)//4
self.inputAccessoryView = toolBar//5
}
}
For more detail: visit full documentation
to hide the keyboard touch on any part outside the textbox or textviews in swift 4 use this peace of code in the ViewController class:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
view.endEditing(true)
super.touchesBegan(touches, with event: event)
}
Regards
Building on the answers of others (kudos!), here is my minimalistic take on it:
import UIKit
extension UITextView {
func withDoneButton(toolBarHeight: CGFloat = 44) {
guard UIDevice.current.userInterfaceIdiom == .phone else {
print("Adding Done button to the keyboard makes sense only on iPhones")
return
}
let toolBar = UIToolbar(frame: CGRect(x: 0.0, y: 0.0, width: UIScreen.main.bounds.width, height: toolBarHeight))
let flexibleSpace = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
let doneButton = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(endEditing))
toolBar.setItems([flexibleSpace, doneButton], animated: false)
inputAccessoryView = toolBar
}
}
Usage:
override func viewDidLoad() {
super.viewDidLoad()
textView.withDoneButton()
}