|
import UIKit |
|
import PlaygroundSupport |
|
|
|
extension String { |
|
subscript(range: NSRange) -> String? { |
|
guard range.location + range.length <= self.characters.count else { |
|
return nil |
|
} |
|
|
|
let r = index(startIndex, offsetBy: range.location)..<index(startIndex, offsetBy: range.location + range.length) |
|
|
|
return self[r] |
|
} |
|
|
|
subscript(range: CountableRange<Int>) -> String? { |
|
guard range.endIndex <= self.characters.count else { |
|
return nil |
|
} |
|
|
|
let r = index(startIndex, offsetBy: range.startIndex)..<index(startIndex, offsetBy: range.endIndex) |
|
|
|
return self[r] |
|
} |
|
} |
|
|
|
class Tagger: NSObject, UITextFieldDelegate { |
|
var tagStart: Int? = nil |
|
var tagRange: CountableRange<Int>? = nil |
|
|
|
func textField(_ textField: UITextField, |
|
shouldChangeCharactersIn range: NSRange, |
|
replacementString string: String) -> Bool { |
|
if textField.text?.characters.count == 1 && range.length == 1 { |
|
tagCancelled() |
|
} |
|
else if tagRange != nil { |
|
if string == " " { |
|
tagCancelled() |
|
} |
|
else if range.location == tagStart { |
|
tagCancelled() |
|
} |
|
else { |
|
tagExtended(to: range.location - range.length) |
|
} |
|
} |
|
else if string == "@" { |
|
if range.location == 0 { |
|
tagStarted(at: range.location) |
|
} |
|
else if let text = textField.text, text.characters.count > 0 { |
|
let sub = text[(range.location - 1)..<range.location] |
|
|
|
print("Previous string: \(String(describing: sub))") |
|
|
|
if sub == " " { |
|
tagStarted(at: range.location) |
|
} |
|
} |
|
} |
|
|
|
print("text: \(string), range: \(NSStringFromRange(range))") |
|
|
|
return true |
|
} |
|
|
|
func tagStarted(at location: Int) { |
|
tagStart = location |
|
tagRange = location..<(location + 1) |
|
|
|
print("Started tag at: \(location), range: \(String(describing: tagRange))") |
|
} |
|
|
|
func tagExtended(to location: Int) { |
|
guard let start = tagStart else { |
|
fatalError("This really shouldn't happen") |
|
} |
|
|
|
tagRange = start..<(location + 1) |
|
|
|
print("Continued tag at: \(location), range: \(String(describing: tagRange))") |
|
} |
|
|
|
func tagCancelled() { |
|
tagStart = nil |
|
tagRange = nil |
|
|
|
print("Tag cancelled") |
|
} |
|
} |
|
|
|
class TextFieldTarget: NSObject { |
|
func setTargetForTextField(tf: UITextField) { |
|
tf.addTarget(self, action: #selector(textFieldChanged(_:)), for: .editingChanged) |
|
} |
|
|
|
func textFieldChanged(_ sender: UITextField) { |
|
if let range = t.tagRange, let text = sender.text { |
|
print("text: \(text[range])") |
|
} |
|
} |
|
} |
|
|
|
let target = TextFieldTarget() |
|
let t = Tagger() |
|
let tf = UITextField(frame: CGRect(x: 0, y: 100, width: 200, height: 30)) |
|
|
|
tf.backgroundColor = .gray |
|
tf.textColor = .white |
|
tf.delegate = t |
|
|
|
target.setTargetForTextField(tf: tf) |
|
|
|
PlaygroundPage.current.liveView = tf |