问题
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?
回答1:
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
回答2:
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.
回答3:
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
}
回答4:
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.
回答5:
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.
来源:https://stackoverflow.com/questions/45667948/how-to-add-followed-text-to-uitextfield