How do I programmatically add a suffix ("kr") to an UITextView?
Thanks!
I assume this is when the user is typing to indicate a currency value.
In viewDidLoad assign your textview delegate (or wherever you wish to assign the delegate):
override func viewDidLoad() {
super.viewDidLoad()
myTextView.delegate = self
}
And then to add the suffix to the textview when user is typing
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
if string.characters.count > 0 {
amountTypedString += string
let newString = amountTypedString + "kr"
myTextView.text = newString
} else {
amountTypedString = String(amountTypedString.characters.dropLast())
if amountTypedString.characters.count > 0 {
let newString = amountTypedString + "kr"
myTextView.text = newString
} else {
myTextView.text = "0kr"
}
}
return false
}
Related
I have a pop-up window, that shows terms of service & privacy policy for users, how to disable text selection but keep links tappable. screenshot
If I set:
SOLVED: By adding textViewDidChangeSelection method in UIViewDelegate and setting
textView.isSelectable = false
textView.isUserInteractionEnabled = true
textView.isSelectable = true
Links become uninteractable.
How do I keep the link tappable and text not selectable?
My whole class:
// HyperLinkTextView.swift
import SwiftUI
struct Hyperlink {
var word: String
var url: NSURL
}
struct HyperLinkTextView: UIViewRepresentable {
private var text: String
private var links: [Hyperlink]
let textView = UITextView()
init(text: String, links: [Hyperlink]) {
self.text = text
self.links = links
}
func makeUIView(context: Self.Context) -> UITextView {
let attributedString = NSMutableAttributedString(string: text)
links.forEach { hyperlink in
let linkAttributes = [NSAttributedString.Key.link: hyperlink.url]
var nsRange = NSMakeRange(0, 0)
if let range = text.range(of: hyperlink.word) {
nsRange = NSRange(range, in: text)
}
attributedString.setAttributes(linkAttributes, range: nsRange)
attributedString.addAttribute(NSAttributedString.Key.underlineStyle, value: NSNumber(value: 1), range: nsRange)
}
textView.isEditable = false
textView.delegate = context.coordinator
textView.attributedText = attributedString
textView.linkTextAttributes = [NSAttributedString.Key.foregroundColor: UIColor(Theme.colorClickables)]
textView.isUserInteractionEnabled = true
textView.isSelectable = true
return textView
}
func updateUIView(_ uiView: UITextView, context: Context) {
uiView.font = UIFont(name: "ArialMT", size: 18)
}
func makeCoordinator() -> Coordinator {
Coordinator()
}
public class Coordinator: NSObject, UITextViewDelegate, NSLayoutManagerDelegate {
weak var textView: UITextView?
public func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange, interaction: UITextItemInteraction, replacementText text: String) -> Bool {
return true
}
func textViewDidChangeSelection(_ textView: UITextView) {
if textView.selectedTextRange != nil {
textView.delegate = nil
textView.selectedTextRange = nil
textView.delegate = self
}
}
}
}
Add this UITextViewDelegate textViewDidChangeSelection and comment out isEditable and isSelectable:
func textViewDidChangeSelection(_ textView: UITextView) {
if textView.selectedTextRange != nil {
textView.delegate = nil
textView.selectedTextRange = nil
textView.delegate = self
}
}
I created textField and want to format text in it like 43/35. Number / number - for credit card month and year.
Can I use number fomatter for it or how can I do it more easily?
The issue here that I need to replace 3rd character if I add new character and remove it if I remove 2nd one.
I do not want use any 3rd party library, I need native implementation
This is my current solution. Basically you need to:
1) Implement the delegate of your textfield somewhere (in my code below I implemented on the ViewController)
2) Implement textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool.
To apply the mask, I created some extensions for String and Characters, as you can see at the end of the following code:
class ViewController: UIViewController {
#IBOutlet weak var textfield: UITextField!
let mask = "##/##"
override func viewDidLoad() {
super.viewDidLoad()
textfield.delegate = self
}
}
extension ViewController: UITextFieldDelegate {
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
guard let normalText = textField.text else { return false }
let beginning = textField.beginningOfDocument
// save cursor location
let cursorLocation = textField.positionFromPosition(beginning, offset: range.location + string.characters.count)
let newString = (normalText as NSString).stringByReplacingCharactersInRange(range, withString: string)
let newStringClean = newString.stringWithOnlyNumbers().withMask(mask)
guard newString != newStringClean else { return true }
textField.text = newStringClean
guard string != "" else { return false }
// fix cursor location after changing textfield.text
if let cL = cursorLocation {
let textRange = textField.textRangeFromPosition(cL, toPosition: cL)
textField.selectedTextRange = textRange
}
return false
}
}
extension String {
func stringWithOnlyNumbers() -> String {
return self.characters.reduce("") { (acc, c) -> String in
guard c.isDigit() else { return acc }
return "\(acc)\(c)"
}
}
func withMask(mask: String) -> String {
var resultString = String()
let chars = self.characters
let maskChars = mask.characters
var stringIndex = chars.startIndex
var maskIndex = mask.startIndex
while stringIndex < chars.endIndex && maskIndex < maskChars.endIndex {
if (maskChars[maskIndex] == "#") {
resultString.append(chars[stringIndex])
stringIndex = stringIndex.successor()
} else {
resultString.append(maskChars[maskIndex])
}
maskIndex = maskIndex.successor()
}
return resultString
}
}
extension Character {
func isDigit() -> Bool {
let s = String(self).unicodeScalars
let uni = s[s.startIndex]
let digits = NSCharacterSet.decimalDigitCharacterSet()
let isADigit = digits.longCharacterIsMember(uni.value)
return isADigit
}
}
Swift 4:
extension CodeEnterViewController: UITextFieldDelegate {
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
guard let normalText = textField.text else { return false }
let beginning = textField.beginningOfDocument
// save cursor location
let cursorLocation = textField.position(from: beginning, offset: range.location + string.count)
let newString = (normalText as NSString).replacingCharacters(in: range, with: string)
let newStringClean = newString.stringWithOnlyNumbers().withMask(mask: mask)
guard newString != newStringClean else { return true }
textField.text = newStringClean
guard string != "" else { return false }
// fix cursor location after changing textfield.text
if let cL = cursorLocation {
let textRange = textField.textRange(from: cL, to: cL)
textField.selectedTextRange = textRange
}
return false
}
}
String
extension String {
func stringWithOnlyNumbers() -> String {
return self.reduce("") { (acc, c) -> String in
guard c.isDigit() else { return acc }
return "\(acc)\(c)"
}
}
func withMask(mask: String) -> String {
var resultString = String()
let chars = self
let maskChars = mask
var stringIndex = chars.startIndex
var maskIndex = mask.startIndex
while stringIndex < chars.endIndex && maskIndex < maskChars.endIndex {
if (maskChars[maskIndex] == "#") {
resultString.append(chars[stringIndex])
stringIndex = chars.index(after: stringIndex)
} else {
resultString.append(maskChars[maskIndex])
}
maskIndex = chars.index(after: maskIndex)
}
return resultString
}
}
Character
extension Character {
func isDigit() -> Bool {
let s = String(self).unicodeScalars
let uni = s[s.startIndex]
let digits = NSCharacterSet.decimalDigits
let isADigit = digits.hasMember(inPlane: UInt8(uni.value))
return isADigit
} }
I'm trying to put in placeholders into my UITextViews, and found a solution here using this chunk of code:
func textView(textView: UITextView, shouldChangeTextInRange range: NSRange, replacementText text: String) -> Bool {
let currentText:NSString = storyView.text
let updatedText = currentText.stringByReplacingCharactersInRange(range, withString:text)
if updatedText.isEmpty {
storyView.text = "Write out your story here!"
storyView.textColor = UIColor.lightGrayColor()
storyView.selectedTextRange = storyView.textRangeFromPosition(storyView.beginningOfDocument, toPosition: storyView.beginningOfDocument)
return false
}else if storyView.textColor == UIColor.lightGrayColor() && !text.isEmpty {
storyView.text = nil
storyView.textColor = UIColor.blackColor()
}
return true
}
func textViewDidChangeSelection(storyView: UITextView) {
if self.view.window != nil {
if storyView.textColor == UIColor.lightGrayColor() {
storyView.selectedTextRange = storyView.textRangeFromPosition(storyView.beginningOfDocument, toPosition: storyView.beginningOfDocument)
}
}
}
It works, and I'm happy with it, but I'd like to apply it to other UITextViews in the same ViewController. How would I do it without creating a duplicate instance of the textView method?
You can use textField.tag in your shouldChangeTextInRange method.
Give tag to your textField of same viewcontroller. And use that tag into shouldChangeTextInRange method.
func textView(textView: UITextView, shouldChangeTextInRange range: NSRange, replacementText text: String) -> Bool {
let currentText:NSString = storyView.text
let updatedText = currentText.stringByReplacingCharactersInRange(range, withString:text)
if updatedText.isEmpty {
if (textField.tag==0)//
{
storyView.text = "Write out your story here!" // stuff
}
else if(textField.tag==1)//
{
storyView.text = "Example 2 "// stuff
}
storyView.textColor = UIColor.lightGrayColor()
storyView.selectedTextRange = storyView.textRangeFromPosition(storyView.beginningOfDocument, toPosition: storyView.beginningOfDocument)
return false
}else if storyView.textColor == UIColor.lightGrayColor() && !text.isEmpty {
storyView.text = nil
storyView.textColor = UIColor.blackColor()
}
return true
}
func textViewDidChangeSelection(storyView: UITextView) {
if self.view.window != nil {
if storyView.textColor == UIColor.lightGrayColor() {
storyView.selectedTextRange = storyView.textRangeFromPosition(storyView.beginningOfDocument, toPosition: storyView.beginningOfDocument)
}
}
}
May be it will help you.
I have followed this tutorial on how to restrict textField to a certain length and character set.
Here is my code, and yet the delegate isn't called:
VC class: this is called (verified while debugging)
var textManager = TextManager()
#IBOutlet weak var enterName_text: UITextField!
func onVideDidLoad() {
...
enterName_text.placeholder = StringConstans.name
enterName_text.delegate = textManager
}
TextManager class:
import Foundation
public class TextManager: NSObject, UITextFieldDelegate {
public func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
if string.characters.count == 0 {
return true
}
let currentText = textField.text ?? ""
let prospectiveText = (currentText as NSString).stringByReplacingCharactersInRange(range, withString: string)
return prospectiveText.containsOnlyCharactersIn("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz") &&
prospectiveText.characters.count <= 6
}
}
The code below works. How does yours differ?
class ViewController: UIViewController {
#IBOutlet weak var textField: UITextField!
let textManager = TextManager()
override func viewDidLoad() {
super.viewDidLoad()
textField.delegate = textManager
}
}
public class TextManager: NSObject, UITextFieldDelegate {
public func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
print("should")
if string.characters.count == 0 {
return true
}
let currentText = textField.text ?? ""
let prospectiveText = (currentText as NSString).stringByReplacingCharactersInRange(range, withString: string)
return prospectiveText.containsOnlyCharactersIn("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz") &&
prospectiveText.characters.count <= 6
}
}
extension String {
func containsOnlyCharactersIn(matchCharacters: String) -> Bool {
let disallowedCharacterSet = NSCharacterSet(charactersInString: matchCharacters).invertedSet
return self.rangeOfCharacterFromSet(disallowedCharacterSet) == nil
}
}
How can I set characters remaining on a UILabel for the UITextView?
I have done this for the UITextField, but the same code does not work..
This is what I have tried:
func textView(textView: UITextView, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool
{
if string == ""
{
if plainTextView.text!.characters.count == 0
{
charCount = 0
countLabel.text = String(format: "%i Characters Left", maxLength - charCount)
return false
}
charCount = (plainTextView.text!.characters.count - 1)
countLabel.text = String(format: "%i Characters Left", maxLength - charCount)
return true
}
else
{
charCount = (plainTextView.text!.characters.count + 1)
countLabel.text = String(format: "%i Characters Left", maxLength - charCount)
if charCount >= maxLength + 1
{
charCount = maxLength
countLabel.text = String(format: "%i Characters Left", maxLength - charCount)
return false;
}
}
return true
}
Any suggestions?
try this
import UIKit
class ViewController: UITextViewDelegate {
let maxLenghth = 200
func textViewDidChange(_ textView: UITextView) {
countLabel.text = "\(maxLength - textView.text.count)"
}
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
return textView.text.count + (text.count - range.length) <= maxLength
}
}
Swift 5
extension AddProperty2ViewController: UITextViewDelegate {
func textViewDidChange(_ textView: UITextView) {
p_summary_line_textview.text = "\(5000 - textView.text.count)"
}
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
return textView.text.count + (text.count - range.length) <= 5000
}
}
func textViewDidBeginEditing(_ textView: UITextView) {
shrinkText()
}
func textViewDidChange(_ textView: UITextView) {
shrinkText()
}
func textViewDidEndEditing(_ textView: UITextView) {
shrinkText()
}
func shrinkText() {
sendButton.isEnabled = reviewTextField.text == "" ? false : true
var text = ""
var counter = 0
if reviewTextField.text.count > limit {
reviewTextField.text.forEach {
if counter < limit {
counter += 1
text.append($0)
} else {
return
}
}
reviewTextField.text = text
}
counterLabel.text = "\(reviewTextField.text.count) / \(limit)"
}