Add a prefix to UITextField - objective-c

I want a '$' Sign in the text field which cannot be erased. A user should be able to enter values after it and if he presses backspace it should only erase the value he entered.
The reason I can't use UILabel before it is because the text in UITextfield is center aligned and it grows to either side as the user enters values.
Please provide any solution.

Set the text field's text to "$" initially.
Set the text field's delegate to some object of yours (probably the view controller containing it).
Implement -textField:shouldChangeCharactersInRange:replacementString: and return NO if the proposed change would delete the "$".
Also, you might need to implement -textFieldDidBeginEditing: to position the cursor after the "$" if it's not there already.

No need to use shouldChangeCharactersInRange delegate method. The easiest and most convenient solution is this
var label = UILabel(frame: CGRectMake(0, 0, 50, 50))
label.text = "$";
label.backgroundColor = UIColor(white: 0.0, alpha: 0.0)
tenantField.rightViewMode = .Always
tenantField.rightView = label

As noted in the previous answer Add a prefix to UITextField
inside the textFieldDidBeginEditing as delegate method i found using the following code to be best and right way to format string we are pre/post fixing currency symbols/codes
NSNumberFormatter* formatter = [[NSNumberFormatter alloc]init];
[formatter setNumberStyle:NSNumberFormatterCurrencyStyle];
[formatter setPaddingPosition:NSNumberFormatterPadAfterPrefix];
[formatter setCurrencySymbol:symbol];
[formatter setMaximumFractionDigits:2];
[formatter setMinimumFractionDigits:2];
[formatter setUsesGroupingSeparator:YES];
[formatter setCurrencyGroupingSeparator:#","];
[formatter setCurrencyDecimalSeparator:#"."];

You can subclass UITextField for all the text field which take amount as value.
class SpecialTextField: UITextField {
var amount: String {
get {
if self._amount.characters.count > 1 {
return (self._amount as NSString).substringFromIndex(2)
}
return ""
}
}
// MARK: - UITextField Observing
override internal func willMoveToSuperview(newSuperview: UIView!) {
if newSuperview != nil {
keyboardType = .DecimalPad
addTarget(self, action: #selector(SpecialTextField.didChangeText), forControlEvents: .EditingChanged)
}
}
override internal func canPerformAction(action: Selector, withSender sender: AnyObject?) -> Bool {
return false
}
private var newVal: String = ""
private var _amount: String = ""
func didChangeText() {
if (text?.characters.count > 2) {
if text?.rangeOfString("$ ") == nil {
text = newVal
}
}
if (text?.characters.count == 1) && (text?.characters.count > newVal.characters.count) {
text = "$ " + text!
}
if (text?.characters.count == 2) && (text?.characters.count < newVal.characters.count) {
text = ""
}
newVal = text ?? ""
_amount = newVal
}
}
This will insert a $ sign whenever first number is entered, and removes it whenever all the numbers are removed.
var amount will give the actual amount value user has entered, after removing the $ sign.
Hope this helped.

func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let updatedText = textField.text!.replacingCharacters(in: Range(range, in: textField.text!)!, with: string)
return updatedText.hasPrefix("yourprefixhere")
}

Related

Swift - enum with button.tag?

i have this code in ObjC
and i want or trying to convert it to swift
typedef NS_ENUM(NSInteger, BB3Photo) {
kirkenType = 10 ,
festenType = 20 ,
praestType = 30
};
#property (nonatomic, assign) BB3Photo selectedPhotoType;
- (IBAction)changeImage:(id)sender {
if ([sender isKindOfClass:[UIButton class]]) {
UIButton *button = sender;
_selectedPhotoType = button.tag;
}
UIActionSheet *sheet = [[UIActionSheet alloc] initWithTitle:#"Vælg Billed"
delegate:self
cancelButtonTitle:nil
destructiveButtonTitle:nil
otherButtonTitles:#"Vælg fra Biblioteket", #"Vælg Kamera", nil];
sheet.actionSheetStyle = UIActionSheetStyleDefault;
[sheet showInView:[self.view window]];
}
here's what i have made from it
enum BBPhoto1: Int {
case kommunen = 10
case sagsbehandler = 20
case festen = 30
}
var selectedPhoto = BBPhoto1.self
#IBAction func changeImage(sender: AnyObject){
if sender .isKindOfClass(UIButton){
let button: UIButton = sender as UIButton
selectedPHoto = (sender as UIButton).tag as BBPhoto1 // doesent work "cannot assign that result as expression"
selectedPHoto = button.tag // doesnt work either "cannot assign that result as expression"
self.selectedPhoto = BBPhoto1.fromRaw((sender as UIButton).tag) // nope "cannot convert the expressions type () to type UIButton"
}
}
i want to be able to have a switch statement with button tags to the same funktion but different in the code
You want to use the tag as the raw value of your BBPhoto1 enum. You can do this with conditional unwrapping:
#IBAction func changeImage(sender: AnyObject){
if let button = sender as UIButton {
if let photoType = BBPhoto1.fromRaw(button.tag) {
self.selectedPhoto = photoType
}
}
}
There's also a problem with the declaration of your selectedPhoto property. It should be:
var selectedPhoto: BBPhoto1?
The way you have it now it doesn't hold a BBPhoto1 value, but instead the type of BBPhoto1 itself.
Note that the fromRaw syntax has changed to an initializer in Xcode 6.1:
#IBAction func changeImage(sender: AnyObject){
if let button = sender as UIButton {
if let photoType = BBPhoto1(rawValue: button.tag) {
self.selectedPhoto = photoType
}
}
}
How about:
#IBAction func changeImage(sender: AnyObject){
if sender .isKindOfClass(UIButton){
let button: UIButton = sender as UIButton
selectedPHoto = BBPhoto1.fromRaw(button.tag)
}
}
or (shorter):
#IBAction func changeImage(sender: UIButton){
selectedPHoto = BBPhoto1.fromRaw(sender.tag)
}
Ok, Not the same code; as in validating a text field, but the same principle in Swift 4, iOS 11, xCode 9.2.
enum SFT: Int {
case StudentNameFieldTag
case StudentIDFieldTag
case StudentClassFieldTag
case ChallengeCodeFieldTag
}
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if textField.tag == SFT.StudentClassFieldTag.rawValue {
let whitespaceSet = CharacterSet.whitespaces
if let _ = string.rangeOfCharacter(from: whitespaceSet) {
return false
} else {
return true
}
}
return true
}
The fromRaw() method returns an optional, so you have to declare your property as optional:
var selectedPHoto: BBPhoto1?
and use this code:
self.selectedPhoto = BBPhoto1.fromRaw((sender as UIButton).tag)
Alternatively, you can unwrap the fromRaw return value:
self.selectedPHoto = BBPhoto1.fromRaw((sender as UIButton).tag)!
Note however that in this case, if the raw value doesn't map to an enum, a runtime exception will be raised.

Append to NSTextView and scroll

OK, what I need should have been very simple. However, I've looked everywhere and I'm not sure I've found something that works 100% (and it's something that has troubled me in the past too).
So, here we are :
I want to be able to append to an NSTextView
After appending, the NSTextView should scroll down (so that that latest appended contents are visible)
Rather straightforward, huh?
So... any ideas? (A code example that performs exactly this simple "trick" would be more than ideal...)
After cross-referencing several answers and sources (with some tweaks), here's the answer that does work (given _myTextView is an NSTextView outlet) :
- (void)appendToMyTextView:(NSString*)text
{
dispatch_async(dispatch_get_main_queue(), ^{
NSAttributedString* attr = [[NSAttributedString alloc] initWithString:text];
[[_myTextView textStorage] appendAttributedString:attr];
[_myTextView scrollRangeToVisible:NSMakeRange([[_myTextView string] length], 0)];
});
}
The appendAttributedString and scrollToEndOfDocument are available starting in OS X 10.0 and 10.6 respectively
extension NSTextView {
func append(string: String) {
self.textStorage?.appendAttributedString(NSAttributedString(string: string))
self.scrollToEndOfDocument(nil)
}
}
Simply use this way :
for (NSInteger i=1; i<=100; i++) {
[self.textView setString:[NSString stringWithFormat:#"%#\n%#",[self.textView string],#(i)]];
}
[self.textView scrollRangeToVisible:NSMakeRange([[self.textView string] length], 0)];
Here's a Swift version of Anoop Vaidya's answer
extension NSTextView {
func append(string: String) {
let oldString = self.string == nil ? "" : self.string!
let newString = NSString(format: "%#%#", oldString, string)
self.string = newString
}
}
Here's a Swiftier solution:
extension NSTextView {
func appendString(string:String) {
self.string! += string
self.scrollRangeToVisible(NSRange(location:countElements(self.string!), length: 0))
}
}

Password validation in UITextField in iOS

I have 1 UITextfield for password in my iPhone application.
I want to validate this textfield with the following validation.
Must be at least 10 characters
Must contain at least one lower case letter, one upper case letter, one digit and one special character
Valid special characters are – ##$%^&+=^.*(?=.{10,})(?=.*d)(?=.*[a-z])(?=.*[A-Z])(?=.*[##$%^&+=]).*$
How can I restrict the UITextField with above requirements?
This is how I would do it. The validation should be done at the end when the user has typed in the password and not in between.I will not be using NSRegularExpression.
-(void)textFieldDidEndEditing:(UITextField *)textField{
int numberofCharacters = 0;
BOOL lowerCaseLetter,upperCaseLetter,digit,specialCharacter = 0;
if([textField.text length] >= 10)
{
for (int i = 0; i < [textfield.text length]; i++)
{
unichar c = [textfield.text characterAtIndex:i];
if(!lowerCaseLetter)
{
lowerCaseLetter = [[NSCharacterSet lowercaseLetterCharacterSet] characterIsMember:c];
}
if(!upperCaseLetter)
{
upperCaseLetter = [[NSCharacterSet uppercaseLetterCharacterSet] characterIsMember:c];
}
if(!digit)
{
digit = [[NSCharacterSet decimalDigitCharacterSet] characterIsMember:c];
}
if(!specialCharacter)
{
specialCharacter = [[NSCharacterSet symbolCharacterSet] characterIsMember:c];
}
}
if(specialCharacter && digit && lowerCaseLetter && upperCaseLetter)
{
//do what u want
}
else
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Error"
message:#"Please Ensure that you have at least one lower case letter, one upper case letter, one digit and one special character"
delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert show];
}
}
else
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Error"
message:#"Please Enter at least 10 password"
delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert show];
}
}
Hope this helps...
You can also do this by using Regex. Here are few example I am providing for you:
// *** Validation for Password ***
// "^(?=.*[A-Za-z])(?=.*\\d)[A-Za-z\\d]{8,}$" --> (Minimum 8 characters at least 1 Alphabet and 1 Number)
// "^(?=.*[A-Za-z])(?=.*\\d)(?=.*[$#$!%*#?&])[A-Za-z\\d$#$!%*#?&]{8,16}$" --> (Minimum 8 and Maximum 16 characters at least 1 Alphabet, 1 Number and 1 Special Character)
// "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)[a-zA-Z\\d]{8,}$" --> (Minimum 8 characters at least 1 Uppercase Alphabet, 1 Lowercase Alphabet and 1 Number)
// "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[$#$!%*?&])[A-Za-z\\d$#$!%*?&]{8,}" --> (Minimum 8 characters at least 1 Uppercase Alphabet, 1 Lowercase Alphabet, 1 Number and 1 Special Character)
// "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[$#$!%*?&])[A-Za-z\\d$#$!%*?&]{8,10}" --> (Minimum 8 and Maximum 10 characters at least 1 Uppercase Alphabet, 1 Lowercase Alphabet, 1 Number and 1 Special Character)
Fourth from the list is your case, following code snippet shows how to use it:
-(BOOL)isValidPassword:(NSString *)passwordString
{
NSString *stricterFilterString = #"^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[$#$!%*?&])[A-Za-z\\d$#$!%*?&]{10,}";
NSPredicate *passwordTest = [NSPredicate predicateWithFormat:#"SELF MATCHES %#", stricterFilterString];
return [passwordTest evaluateWithObject:passwordString];
}
Using the method:
if(![self isValidPassword:txtPassword.text]) {
/* Show alert: "Password must be minimum 10 characters,
at least 1 Uppercase Alphabet, 1 Lowercase Alphabet,
1 Number and 1 Special Character" */
}
else {
// Password is valid
}
Condition: Password should contain atleast 8 characters, 1 uppercase and 1 number
Solution in Swift 3
you can write String Extension like this,
extension String {
func isValidPassword() -> Bool {
let regularExpression = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[$#$!%*?&])[A-Za-z\\d$#$!%*?&]{8,}"
let passwordValidation = NSPredicate.init(format: "SELF MATCHES %#", regularExpression)
return passwordValidation.evaluate(with: self)
}
}
//Example 1
var password = "#Abcdef011" //string from UITextField (Password)
password.isValidPassword() // -> true
//Example 2
var password = "Abcdef011" //string from UITextField
password.isValidPassword() // -> false
or you can write function like this,
func validate(password: String) -> Bool
{
let regularExpression = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[$#$!%*?&])[A-Za-z\\d$#$!%*?&]{8,}"
let passwordValidation = NSPredicate.init(format: "SELF MATCHES %#", regularExpression)
return passwordValidation.evaluate(with: password)
}
this will give you the same result.
Swift 3
check if password is strong ?
length more than or equal 8
lowercase
uppercase
decimal Digits
special characters like !##$%^&*()_-+ is optional
Why i not use regular expression ?
Because it's difficult to support reserved characters in regular
expression syntax.
func isValidated(_ password: String) -> Bool {
var lowerCaseLetter: Bool = false
var upperCaseLetter: Bool = false
var digit: Bool = false
var specialCharacter: Bool = false
if password.characters.count >= 8 {
for char in password.unicodeScalars {
if !lowerCaseLetter {
lowerCaseLetter = CharacterSet.lowercaseLetters.contains(char)
}
if !upperCaseLetter {
upperCaseLetter = CharacterSet.uppercaseLetters.contains(char)
}
if !digit {
digit = CharacterSet.decimalDigits.contains(char)
}
if !specialCharacter {
specialCharacter = CharacterSet.punctuationCharacters.contains(char)
}
}
if specialCharacter || (digit && lowerCaseLetter && upperCaseLetter) {
//do what u want
return true
}
else {
return false
}
}
return false
}
let isVaildPass:Bool = isValidated("Test**00+-")
print(isVaildPass)
You can verify your password validation using the below function just pass a password string and this will return you BOOL value.
-(BOOL) isPasswordValid:(NSString *)pwd {
NSCharacterSet *upperCaseChars = [NSCharacterSet characterSetWithCharactersInString:#"ABCDEFGHIJKLKMNOPQRSTUVWXYZ"];
NSCharacterSet *lowerCaseChars = [NSCharacterSet characterSetWithCharactersInString:#"abcdefghijklmnopqrstuvwxyz"];
//NSCharacterSet *numbers = [NSCharacterSet characterSetWithCharactersInString:#"0123456789"];
if ( [pwd length]<6 || [pwd length]>20 )
return NO; // too long or too short
NSRange rang;
rang = [pwd rangeOfCharacterFromSet:[NSCharacterSet letterCharacterSet]];
if ( !rang.length )
return NO; // no letter
rang = [pwd rangeOfCharacterFromSet:[NSCharacterSet decimalDigitCharacterSet]];
if ( !rang.length )
return NO; // no number;
rang = [pwd rangeOfCharacterFromSet:upperCaseChars];
if ( !rang.length )
return NO; // no uppercase letter;
rang = [pwd rangeOfCharacterFromSet:lowerCaseChars];
if ( !rang.length )
return NO; // no lowerCase Chars;
return YES;
}
for me Best way was to use NSPredicate and regex.
this is regex for your case: ^(?=.{10,})(?=.*[0-9])(?=.*[a-zA-Z])([##$%^&=a-zA-Z0-9_-]+)$
objective C code:
NSString *regex = #"^(?=.{10,})(?=.*[0-9])(?=.*[a-zA-Z])([##$%^&=a-zA-Z0-9_-]+)$";
NSPredicate *passwordTest = [NSPredicate predicateWithFormat:#"SELF MATCHES %#", regex];
BOOL isValid = [passwordTest evaluateWithObject:yourTextfield.text];
use a regex (NSRegularExpression class has docs on how to write the patten itself) and then :
- (BOOL)textField:(UITextField *)theTextField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
//delete
if (string.length == 0) {
return YES;
}
if (self.regEx) {
NSMutableString* check = [NSMutableString stringWithString:theTextField.text];
[check replaceCharactersInRange:range withString:string];
NSTextCheckingResult* match = [self.regEx firstMatchInString:check options:0 range:NSMakeRange(0, [check length])];
if (match.range.length != check.length) {
return NO;
}
}
}
Warning: Restricting the input this way is really confusing for users. You type and type and the character you type just doesnt appear!
I'd maybe go with a small red (!) next to the test field but I'd always allow the input itself!
I have this elegant solution for Forms (like sign-up) where you have a lot of validation
I have in my custom UITextField the outlet:
#IBInspectable var regexpValidation: String? = nil
In storyboard I can access it through attribute inspector and put regexp string like that (for email):
[a-z0-9!#$%&'*+/=?^_{|}~-]+(?:.[a-z0-9!#$%&'*+/=?^_{|}~-]+)*#(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?
then in my subclass I have this computed var:
#IBInspectable var regexpValidation: String? = nil // Optional, Set this in InterfaceBuilder
var inputIsValid: Bool {
get {
if let expr = regexpValidation {
return (text.rangeOfString(expr, options: NSStringCompareOptions.RegularExpressionSearch, range: nil, locale: nil) != nil)
} else {
return true
}
}
}
which could be used like this:
override func resignFirstResponder() -> Bool {
if (inputIsValid) {
return super.resignFirstResponder()
}
else {
text = ""
return false
}
}
You need to write your validation code in this delegate method of UITextField
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
Few links that you might want to refer for implementation
how to use regular expression in iOS sdk
iOS TextField Validation
Use the control:isValidObject: method of the NSTextFieldDelegate protocol which allows you to validate the value of a NSTextField. Assuming you have all your interface builder bits and pieces configured correctly, you might do something like this:
#interface PreferencesController : NSWindowController <NSTextFieldDelegate> {
IBOutlet NSTextField *username, *password;
}
#end
#implementation PreferencesController
- (BOOL)control:(NSControl *)control isValidObject:(id)object
{
if (control == password) {
// Perform validation and return YES or NO
}
return YES;
}
#end
SWIFT 5 USING RXSWIFT, a better, neat and reactive approach.
validate password function will be like this, Obviously you can add as many conditions as you want as per your requirement.
func validatePassword(password: String) -> (Bool, String) {
//Minimum 8 characters at least 1 Alphabet and 1 Number:
var tuple: (Bool, String) = (true, "")
var string = "Requires atleast"
if(password.rangeOfCharacter(from: CharacterSet.letters) == nil){
string = "uppercase"
tuple = (false, string)
}
if(password.rangeOfCharacter(from: CharacterSet.decimalDigits) == nil){
string += ", number"
tuple = (false, string)
}
if(password.count < 8 ){
string += ", 8 chars"
tuple = (false, string)
}
return tuple }
func isPasswordValid(in string: String) -> Observable<(Bool, String)> {
return Observable.create { observer -> Disposable in
let tuple = self.validation.validatePasswordForSymbol(password: string)
observer.onNext(tuple)
return Disposables.create()
}
}
You can use aforementioned function in viewModel or VC as per your architecture.
Then invoke the same function like below in your VC.
passwordTextField.rx.text
.orEmpty //1
.filter { $0.count >= 1 } //2
.flatMap { self.isPasswordValid(in: $0) }
.subscribe(onNext: { result in
print("Valid password", result)
//update UI here
// result will be like (false, "Requires atleast, 8 chars, number")
}).disposed(by: disposeBag)

UITEXTVIEW: Get the recent word typed in uitextview

I want to get the most recent word entered by the user from the UITextView.
The user can enter a word anywhere in the UITextView, in the middle or in the end or in the beginning. I would consider it a word when the user finishes typing it and presses a space and does any corrections using the "Suggestions from the UIMenuController".
Example: User types in "kimd" in the UITextView somewhere in the middle of text, he gets a popup for autocorrection "kind" which he does. After he does that, I want to capture "kind" and use it in my application.
I searched a lot on the internet but found solutions that talk about when the user enters text in the end. I also tried detecting a space and then doing a backward search until another space after some text is found, so that i can qualify it as a word. But I think there may be better ways to do this.
I have read somewhere that iOS caches the recent text that we enter in a text field or text view. If I can pop off the top one , that's all I want. I just need handle to that object.
I would really appreciate the help.
Note: The user can enter text anywhere in UItextview. I need the most recent entered word
Thanks.
//This method looks for the recent string entered by user and then takes appropriate action.
- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text {
//Look for Space or any specific string such as a space
if ([text isEqualToString:#" "]) {
NSMutableCharacterSet *workingSet = [[NSCharacterSet whitespaceAndNewlineCharacterSet] mutableCopy];
NSRange newRange = [self.myTextView.text rangeOfCharacterFromSet:workingSet
options:NSBackwardsSearch
range:NSMakeRange(0, (currentLocation - 1))];
//The below code could be done in a better way...
UITextPosition *beginning = myTextView.beginningOfDocument;
UITextPosition *start = [myTextView positionFromPosition:beginning offset:currentLocation];
UITextPosition *end = [myTextView positionFromPosition:beginning offset:newRangeLocation+1];
UITextRange *textRange = [myTextView textRangeFromPosition:end toPosition:start];
NSString* str = [self.myTextView textInRange:textRange];
}
}
Here is what I would suggest doing, might seem a little hacky but it would work just fine:
First in .h conform to the UITextViewDelegate and set your text view's delegate to self like this:
myTextView.delegate = self;
and use this code:
- (void)textViewDidChange:(UITextView *)textView { // Delegate method called when any text is modified
if ([textView.text substringFromIndex: [textView.text length] - 1]) { // Gets last character of the text view's text
NSArray *allWords = [[textView text] componentsSeparatedByString: #" "]; // Gets the text view's text and fills an array with all strings seperated by a space in text view's text, basically all the words
NSString *mostRecentWord = [allWords lastObject]; // The most recent word!
}
}
I use this code to get the word behind the #-sign:
- (void)textViewDidChange:(UITextView *)textView {
NSRange rangeOfLastInsertedCharacter = textView.selectedRange;
rangeOfLastInsertedCharacter.location = MAX(rangeOfLastInsertedCharacter.location - 1,0);
rangeOfLastInsertedCharacter.length = 1;
NSString *lastInsertedSubstring;
NSString *mentionSubString;
if (![textView.text isEqualToString:#""]) {
lastInsertedSubstring = [textView.text substringWithRange:rangeOfLastInsertedCharacter];
if (self.startOfMention > 0 || self.startOfHashtag > 0) {
if ([lastInsertedSubstring isEqualToString:#" "] || (self.startOfMention > textView.selectedRange.location || self.startOfHashtag > textView.selectedRange.location)) {
self.startOfMention = 0;
self.lenthOfMentionSubstring = 0;
}
}
if (self.startOfMention > 0) {
self.lenthOfMentionSubstring = textView.selectedRange.location - self.startOfMention;
NSRange rangeOfMentionSubstring = {self.startOfMention, textView.selectedRange.location - self.startOfMention};
mentionSubString = [textView.text substringWithRange:rangeOfMentionSubstring];
dhDebug(#"mentionSubString: %#", mentionSubString);
UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification, nil);
}
}
}
Simple extension for UITextView:
extension UITextView {
func editedWord() -> String {
let cursorPosition = selectedRange.location
let separationCharacters = NSCharacterSet(charactersInString: " ")
let beginRange = Range(start: text.startIndex.advancedBy(0), end: text.startIndex.advancedBy(cursorPosition))
let endRange = Range(start: text.startIndex.advancedBy(cursorPosition), end: text.startIndex.advancedBy(text.characters.count))
let beginPhrase = text.substringWithRange(beginRange)
let endPhrase = text.substringWithRange(endRange)
let beginWords = beginPhrase.componentsSeparatedByCharactersInSet(separationCharacters)
let endWords = endPhrase.componentsSeparatedByCharactersInSet(separationCharacters)
return beginWords.last! + endWords.first!
}
}

Get selection (highlighted text) string from NSTextView Objective-C

How can I get the selected text's string from a NSTextView as an NSString?
Your help is greatly appreciated.
An NSText can have more than only one selection. Check it out with TextEditapp: select a string with the mouse while pressing CMD. So you can select as many strings as you want. Therefore I think, a more common solution is to use:
NSArray *ranges = [myTextView selectedRanges];
and then extract the strings one by one.
Since NSTextView is a subclass of NSText, you can use NSText instance methods to figure out the selected string like so:
NSString *selected = [[myTextView string]
substringWithRange:[myTextView selectedRange]];
Swift 5, handling multiple selections of NSTextView
based on #vauxhall's answer
extension NSTextView {
var selectedText: String {
var text = ""
for case let range as NSRange in self.selectedRanges {
text.append(string[range]+"\n")
}
text = String(text.dropLast())
return text
}
}
extension String {
subscript (_ range: NSRange) -> Self {
.init(self[index(startIndex, offsetBy: range.lowerBound) ..< index(startIndex, offsetBy: range.upperBound)])
}
}
Swift
extension NSTextView {
var selectedText: String {
string[selectedRange()]
}
}
extension String {
subscript (_ range: NSRange) -> Self {
.init(self[index(startIndex, offsetBy: range.lowerBound) ..< index(startIndex, offsetBy: range.upperBound)])
}
}
Usage
let textView = NSTextView()
print(textView.selectedText)