Multiple UITextView placeholders - ios

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.

Related

How to add followed text to uitextfield

I have one text field with place holder called letschat. Now whenever I start typing in my textfield, I want to show my textfield as some #letschat. When my textfield is empty that time my placeholder have to show. That I did. But I want to set whenever I start typing in my textfield. Whatever I am typing with that I want this text also to visible like:
Some #Lletschat
How can I do this?
I created a UITextField subclass that uses the placeholder (if set) as a suffix. As far as I can see everything works as expected. Maybe there are some tweaks needed to suit your needs.
Feel free to ask if anything is unclear:
class SuffixTextField: UITextField {
override init(frame: CGRect) {
super.init(frame: frame)
sharedInit()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
sharedInit()
}
private func sharedInit() {
addTarget(self, action: #selector(textChanged), for: .editingChanged)
}
override var text: String? {
didSet {
selectedTextRange = maxTextRange
}
}
override var attributedText: NSAttributedString? {
didSet {
selectedTextRange = maxTextRange
}
}
#objc private func textChanged() {
if let currentText = text, let placeholder = placeholder {
if currentText == placeholder {
self.text = nil
} else if !currentText.hasSuffix(placeholder) {
self.text = currentText + placeholder
}
}
}
private var maxCursorPosition: UITextPosition? {
guard let placeholder = placeholder, !placeholder.isEmpty else { return nil }
guard let text = text, !text.isEmpty else { return nil }
return position(from: beginningOfDocument, offset: (text as NSString).range(of: placeholder, options: .backwards).location)
}
private var maxTextRange: UITextRange? {
guard let maxCursorPosition = maxCursorPosition else { return nil }
return textRange(from: maxCursorPosition, to: maxCursorPosition)
}
override var selectedTextRange: UITextRange? {
get { return super.selectedTextRange }
set {
guard let newRange = newValue,
let maxCursorPosition = maxCursorPosition else {
super.selectedTextRange = newValue
return
}
if compare(maxCursorPosition, to: newRange.start) == .orderedAscending {
super.selectedTextRange = textRange(from: maxCursorPosition, to: maxCursorPosition)
} else if compare(maxCursorPosition, to: newRange.end) == .orderedAscending {
super.selectedTextRange = textRange(from: newRange.start, to: maxCursorPosition)
} else {
super.selectedTextRange = newValue
}
}
}
}
here you can see a preview:
https://www.dropbox.com/s/etkbme37wuxbw1q/preview.mov?dl=0
You can take the action of UITextField textFieldDidChange and get the call of every time when the textField text changed.
Just like that:
func textChangedAction(sender:UITextFiled) {
if sender.text.rangeOfString("#Lletschat") != nil{
sender.text = sender.text.replacingOccurrences(of: "#Lletschat", with: "")
}
sender.text = "\(sender.text!) #Lletschat"
}
If you want to changed the color of your specific text you can check here.
Implement textfield delegate like this-
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
textField.text = textField.text?.replacingOccurrences(of: " #\(textField.placeholder!)", with: "", options: .literal, range: nil)
textField.text?.append(string)
textField.text?.append(" #\(textField.placeholder!)")
return false
}
Simple solution:
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
guard let newRange = textField.text?.range(from: range), let result = textField.text?.replacingCharacters(in: newRange, with: string) else { return true }
if result.endsWithString("#letschat") {
return true
} else {
textField.text = result + "#letschat"
let position = textField.position(from: textField.beginningOfDocument, offset: result.characters.count)!
textField.selectedTextRange = textField.textRange(from: position, to: position)
return false
}
}
With helper extension:
extension String {
func range(from oldOne: NSRange) -> Range<String.Index>? {
guard
let from16 = utf16.index(utf16.startIndex, offsetBy: oldOne.location, limitedBy: utf16.endIndex),
let to16 = utf16.index(utf16.startIndex, offsetBy: oldOne.location + oldOne.length, limitedBy: utf16.endIndex),
let from = from16.samePosition(in: self),
let to = to16.samePosition(in: self)
else { return nil }
return from ..< to
}
func endsWithString(_ string: String) -> Bool {
guard characters.count >= string.characters.count else { return false }
let index = self.index(startIndex, offsetBy: characters.count - string.characters.count)
let substring = self.substring(from: index)
return substring == string
}
}
Difficult but clear solution is to create your own UIControl-subclass with UITextField and UILabel children views:
+-----------+ +-----------+
| textfield | -(3px)- | #letschat |
+-----------+ +-----------+
Use autolayout to keep the distance of 3 pixels between it.
Don't forget to configure your class to send all the incoming actions to the textfield. You can use different font colours for these controls so user won't be confused about efforts to change label's value.
Conform your class to UITextfieldDelegate and then assign textfield.delegate = self
Now, add this Delegate Method, if you want your #letschat to be appended after user endtyping.
func textFieldDidEndEditing(textField: UITextField) {
textField.text = "\(textField.text)#letschat"
}
or if you want it at the typing time.
textField.addTarget(self, action: #selector(YourViewController.textFieldDidChange(_:)), forControlEvents: UIControlEvents.EditingChanged)
func textFieldDidChange(textField: UITextField) {
if textField.containsString("#letschat") {
textField.text = textField.text.stringByReplacingOccurrencesOfString("#letschat", withString: "")
}
textField.text = "\(textField.text)#letschat"
}
Hope this helps.

iOS Swift - How to add a uneditable suffix to a UITextView

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
}

Swift. Format input in textField

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

Making a Login screen similar to the lock screen

Im trying to make a login screen similar to the lock screen with 4 textField.
The problem i'm facing is with changing the focus from one text field to the next.
when i say secondTextField.becomeFirstResponder(), the value of the firstTextField gets copied to the secondTextField
i'm using a decimal pad - keyboard type
Here is what i have done so far and i can't find a way around to fix the problem :
Using UITextFieldDelegate
made IBOutlet connection for all the textField
firstText.delegate = self
secondText.delegate = self
thirdText.delegate = self
firstText.becomeFirstResponder()
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
var newLength = countElements(textField.text) + countElements(string) - range.length
if newLength == 1 {
if textField == firstText{
changeFocus(textField)
}else if textField == secondText {
changeFocus(textField)
}else if textField == thirdText {
changeFocus(textField)
}
}
return newLength <= 1
}
func changeFocus(nextFocus: UITextField){
if nextFocus == firstText {
firstText.resignFirstResponder()
secondText.becomeFirstResponder()
} else if nextFocus == secondText {
secondText.resignFirstResponder()
thirdText.becomeFirstResponder()
} else if nextFocus == thirdText {
thirdText.resignFirstResponder()
}
}
How can i do this correctly, any help and suggestion please.
Use this delegate method to change the focus
func textFieldShouldReturn(textField: UITextField) -> Bool
To add a little more to svrushal answer:
func textFieldShouldReturn(textField: UITextField) -> Bool {
if textField == firstText {
secondText.becomeFirstResponder()
}
if textField == secondText {
thirdText.becomeFirstResponder()
}
if textField == thirdText {
fourthText.becomeFirstResponder()
}
}
you can refer this link https://github.com/dhennessy/PAPasscode/blob/master/PAPasscode/PAPasscodeViewController.h
it will help you out.
I found something similar here: similar
Got it to work, with two limitations :
Firstly, I cant delete backwards
Second, the user has to tap on the first textField if the passCode is wrong.
But for now these is good enough.
Here are the methods
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
var shouldProcess = false
var moveToNextTextField = false
var insertStringLength = countElements(string)
if insertStringLength == 0 {
shouldProcess = true
}
else {
if countElements(textField.text) == 0 {
shouldProcess = true
}
}
if shouldProcess {
var myString = textField.text
if countElements(myString) == 0{
myString = myString + string
password = password + myString
moveToNextTextField = true
}
textField.text = myString
if moveToNextTextField {
changeFocus(textField)
}
}
return false
}
method to change the focus :
func changeFocus(nextTextField : UITextField){
if nextTextField == firstText {
firstText.resignFirstResponder()
secondText.becomeFirstResponder()
} else if nextTextField == secondText {
secondText.resignFirstResponder()
thirdText.becomeFirstResponder()
} else if nextTextField == thirdText {
thirdText.resignFirstResponder()
fourthText.becomeFirstResponder()
} else if nextTextField == fourthText {
fourthText.resignFirstResponder()
}
}
Method to check the pin/passcode :
func textFieldShouldEndEditing(textField: UITextField) -> Bool {
if textField == fourthText {
thirdText.resignFirstResponder()
let pass = defaultUser.stringForKey(Admin)
println("password = \(password)")
if pass == password {
println("the password is correct")
dispatch_async(dispatch_get_main_queue()) { () -> Void in
self.performSegueWithIdentifier("SegueToSecond", sender: self)
}
} else {
println("wrong password")
thirdText.text = ""
secondText.text = ""
firstText.text = ""
fourthText.text = ""
password = ""
view.reloadInputViews()
}
}
return true
}
Hope these help some one later on.

Why does textfieldDidEndEditing getting called immediately after textfieldBeginEditing in iOS 13? Works Fine in iOS lower versions

I want to edit the textfield's text and save it whenever user slides on to left on the collection view cell and clicks on rename.So I wrote cell.textfield.becomefirstresponder() whenever rename is clicked..Then In case of handling textfield delegate methods, First textDidBeginEditing is called , then without even doing anything, textfieldDidEndEditing is getting called resigning the responder status.It is happening only in iOS 13
public func textFieldDidBeginEditing(_ textField: UITextField) {
self.emptyView.isHidden = true
}
public func textFieldDidEndEditing(_ textField: UITextField, reason: UITextFieldDidEndEditingReason) {
setChangedValues(selectedTextField: textField)
}
public func textFieldShouldReturn(_ textField: UITextField) -> Bool {
//setChangedValues(selectedTextField: textField)
textField.resignFirstResponder()
return true
}
public func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if range.location == 0 && string == " " {
return false
}
//Fix:- when u remove the text completely and still textfield contains the last character
if range.location == 0 && string == "" {
textField.text = ""
}
//Setting the limit
var maxLimit = 50
if textField.tag == 2 {
maxLimit = 15
}
let newLength = (textField.text?.count)! + string.count - range.length
if newLength <= maxLimit {
//If new character is typed
} else {
//If the entered character is more than the limit, return false
return newLength<=maxLimit
}
return true
}
func setChangedValues(selectedTextField: UITextField) {
if (selectedTextField.text?.isEmpty)! {
assetsTableView.reloadData()
} else {
var assetModels = Array<FHTeamAssetsModel>()
if isEdit {
//Both are different, please update
if selectedTextField.text != assetsList[selectedAsset!][WPEConstants.ASSET__NAME]! {
//Getting the asset based on asset list selection
let asset = FHBase.getAssetModel(id: assetsList[selectedAsset!][WPEConstants.ASSET__ID]!)
asset.name = selectedTextField.text
FHBase.updateTeamAssets(model: asset)
}
if selectedPath > 0 {
//Getting the updated sub asset list
assetModels = FHBase.getTeamAssetsModels(assetId: assetsPathArray[selectedPath][WPEConstants.ASSET_ID])
} else {
//Getting the updated asset list(without assetId)
assetModels = FHBase.getTeamAssetsModels(assetId: nil)
}
} else {
if selectedPath > 0 {
//Creating a new sub-asset, with assetId
FHBase.shared.createAssetsModel(name: selectedTextField.text, assetId: assetsPathArray[selectedPath][WPEConstants.ASSET_ID], assetName: assetsPathArray[selectedPath][WPEConstants.ASSET_NAME])
//Getting the updated sub asset list
assetModels = FHBase.getTeamAssetsModels(assetId: assetsPathArray[selectedPath][WPEConstants.ASSET_ID])
} else {
//Creating a new asset(without assetId)
FHBase.shared.createAssetsModel(name: selectedTextField.text, assetId: nil, assetName: nil)
//Getting the updated asset list(without assetId)
assetModels = FHBase.getTeamAssetsModels(assetId: nil)
}
}
//Creating asset list
self.createAssetsList(assetModels: assetModels)
assetsTableView.reloadData()
self.isEdit = false
}
self.updateEmptyStateUI()
}
Check whether somebody disable textField!

Resources