How to check for Delete character in keyDown function? - objective-c

In my custom NSView, I am looking to detect the Delete key press. Here's my code -
- (void)keyDown:(NSEvent *)theEvent {
NSString * characters = theEvent.characters;
if (characters != nil && characters.length > 0) {
NSString* firstchar = [characters substringToIndex:1];
//0x007F is the unicode delete character
if ([firstchar isEqualToString:[NSString stringWithFormat:#"%C", 0x007F]]) {
//delete character detected, do something
return;
}
}
[super keyDown:theEvent];
}
Doesn't work!! The delete character isn't detected.
Update: the following code works!
- (void)keyDown:(NSEvent *)event {
unichar key = [[event charactersIgnoringModifiers] characterAtIndex:0];
if(key == NSDeleteCharacter || key == NSBackspaceCharacter || key == NSDeleteFunctionKey)
{
//delete character detected, do something
return;
}
[super keyDown:event];
}

Related

Making autocomplete case-insensitive

I'm implementing an autocomplete in cocoa for an OSX application and thus far I've got it all pinned down. The one hangup is that the autocomplete is case-sensitive and that's not really what I want/need. Ideally the autocomplete will be case INSENSITIVE. Relevant code below:
#implementation autocompleteController
- (void)viewDidLoad {
[super viewDidLoad];
self.textField.delegate = self;
}
-(void)controlTextDidChange:(NSNotification *)obj{
NSTextView * fieldEditor = [[obj userInfo] objectForKey:#"NSFieldEditor"];
if (self.isAutocompleting == NO && !self.backspaceKey) {
self.isAutocompleting = YES;
self.lastEntry = [[[fieldEditor string] capitalizedString] copy];
[fieldEditor complete:nil];
self.isAutocompleting = NO;
}
if (self.backspaceKey) {
self.backspaceKey = NO;
}
}
-(NSArray *)control:(NSControl *)control textView:(NSTextView *)textView completions:(NSArray *)words forPartialWordRange:(NSRange)charRange indexOfSelectedItem:(NSInteger *)index{
NSMutableArray * suggestions = [NSMutableArray array];
NSArray * possibleStrings = #[#"TEST", #"ABC", #"abc", #"amauroy", #"AMA", #"amazing"];
if (!self.lastEntry || !possibleStrings) {
return nil;
}
for (NSString * string in possibleStrings) {
if ([string hasPrefix:self.lastEntry]) {
[suggestions addObject:string];
}
}
return suggestions;
}
-(BOOL)control:(NSControl *)control textView:(NSTextView *)textView doCommandBySelector:(SEL)commandSelector{
if (commandSelector == #selector(deleteBackward:)) {
self.backspaceKey = YES;
}
return NO;
}
#end
As Pro Blaster points out, the problem is with the following line:
if ([string hasPrefix:self.lastEntry]) {
Your autocompletion is case-sensitive because -hasPrefix: is case-sensitive. One approach is to convert everything to lower case (upper case would also work, of course). Another is to write a case-insensitive version of -hasPrefix: and add it to NSString using a category, like this:
#interface NSString (autocomplete)
- (BOOL)hasPrefixIgnoringCase:(NSString*)aString;
#end;
#implementation NSString (autocomplete)
- (BOOL)hasPrefixIgnoringCase:(NSString*)aString
{
NSRange *prefix = [self rangeOfString:aString options:NSCaseInsensitiveSearch];
return prefix.location == 0 && prefix.length == aString.length;
}
#end
Then use that method in your code:
if ([string hasPrefixIgnoringCase:self.lastEntry]) {
Note: The provided code is untested. The concept is sound, but you may find a syntax error or two.
I did this once.
You would do so by replacing :
for (NSString * string in possibleStrings) {
if ([string hasPrefix:self.lastEntry]) {
[suggestions addObject:string];
}
}
return suggestions
with:
for (NSString * string in possibleStrings) {
if ([[string lowercaseString] hasPrefix:[self.lastEntry lowercaseString]]) {
[suggestions addObject:string];
}
}
return suggestions;

Strange NSFormatter behavior

I have TextField with value binded to Document's serverAddress property (readwrite, copy), and TextField formatter delegate connected with ServerAddressFormatter object in XIB.
It's actually working with input like 127.0.0.1:8080, but as soon as I put something without : TextField clear itself completely.
Here's ServerAddressFormatter implementation:
#implementation ServerAddressFormatter
- (NSString *) stringForObjectValue:(NSArray *)obj {
if ([obj isKindOfClass:[NSArray class]]) {
return [obj componentsJoinedByString:#":"];
} else {
return #"";
}
}
- (BOOL)getObjectValue:(out id *)anObject
forString:(NSString *)string
errorDescription:(out NSString **)error {
int i;
for (i = (int) ([string length] == 0 ? 0 : [string length] - 1); i > 0; i--) {
if ([string characterAtIndex:i] == ':') break;
}
if (i == 0) {
*anObject = #[string]; // if I put string, #"100" here it's working fine
} else {
*anObject = #[[string substringToIndex:i], [string substringFromIndex:i+1]];
}
return YES;
}
#end

How to restrict numbers and special characters in objective-c [duplicate]

This question already has answers here:
Restrict NSTextField to only allow numbers
(10 answers)
Closed 8 years ago.
In textfield I want to restrict numbers like (1234567890) and special characters but I want to allow alphanumeric characters. How I am suppose to do this?
Use the UITextField delegate method
textField:shouldChangeCharactersInRange:replacementString:
To check the string that is about to be replaced, if you allow it then return yes if not then return no.
Here is some more information.
Apple UITextField Delegate
try following code
+ (BOOL)isNumber:(NSString *)value {
if ( (value == nil) || ([#"" isEqualToString:value]) ) {
return NO;
}
int l = [value length];
BOOL b = NO;
for (int i = 0; i < l; i++) {
NSString *str =
[[value substringFromIndex:i]
substringToIndex:1];
const char *c =
[str cStringUsingEncoding:
NSASCIIStringEncoding];
if ( c == NULL ) {
b = NO;
break;
}
if ((c[0] >= 0x30) && (c[0] <= 0x39)) {
b = YES;
} else {
b = NO;
break;
}
}
if (b) {
return YES;
} else {
return NO;
}
}
-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
if ( (string != nil) && (string != #"") ) {
if (![self isNumber:string]) {
return NO;
}
}
return YES;
}
You need to write a NSFormatter and assign it to your text field. Here an example implementation of a such NSFormatter which uses a NSRegularExpression to validate the NSTextField contents.
#interface XXNameElementFormatter : NSFormatter
#end
#implementation HcNameElementFormatter {
NSRegularExpression *_re;
}
- (id)init {
self = [super init];
if (self) {
[self initRegularExpression];
}
return self;
}
- (void)awakeFromNib
{
[self initRegularExpression];
}
- (void)initRegularExpression
{
NSError *reError;
_re = [NSRegularExpression regularExpressionWithPattern:#"^[a-z]*$" options:NSRegularExpressionCaseInsensitive error:&reError];
NSAssert(_re != nil, #"Error in regular expression, error: %#", reError);
}
- (NSString *)stringForObjectValue:(id)obj
{
return obj;
}
- (BOOL)getObjectValue:(out __autoreleasing id *)obj forString:(NSString *)string errorDescription:(out NSString *__autoreleasing *)error
{
*obj = string;
return YES;
}
- (BOOL)isPartialStringValid:(NSString *__autoreleasing *)partialStringPtr proposedSelectedRange:(NSRangePointer)proposedSelRangePtr originalString:(NSString *)origString originalSelectedRange:(NSRange)origSelRange errorDescription:(NSString *__autoreleasing *)error
{
NSParameterAssert(partialStringPtr != nil);
NSString *partialString = *partialStringPtr;
NSRange firstMatch = [_re rangeOfFirstMatchInString:*partialStringPtr options:0 range:NSMakeRange(0, partialString.length)];
return firstMatch.location != NSNotFound;
}
#end

Allow only alpha numeric characters in UITextView

Is there anyway i can allow user to enter only alpha numeric characters in a text view and no other character.
EDIT:
Tried,
if ([_txtView.text rangeOfCharacterFromSet:alphaSet].location != NSNotFound)
{
UIAlertView* alert = [[UIAlertView alloc] initWithTitle:#"Hello" message:#"Only alpha numeric characters are allowed" delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert show];
return;
}
but this only works for some of the times
Thanks!!
You can achieve that using [[[NSCharacterSet alphanumericCharacterSet] invertedSet]. This method will return a character set containing only characters that don’t exist in the receiver.
NSCharacterSet *charactersToBlock = [[NSCharacterSet alphanumericCharacterSet] invertedSet];
//Conform UITextField delegate and implement this method.
- (BOOL)textField:(UITextField *)field shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)characters
{
return ([characters rangeOfCharacterFromSet:charactersToBlock].location == NSNotFound);
}
Try this:
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
if (textField == txtWebsite) {
NSCharacterSet *set = [NSCharacterSet characterSetWithCharactersInString:#"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890 "];
if ([string rangeOfCharacterFromSet:set].location != NSNotFound) {
return YES;
}
else {
return NO;
}
}
else {
return YES;
}
}
write code in delegate method of uitextfield.
set delegate for textview and override/implement test should change in range method
- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text
{
NSCharacterSet *alphaSet = [NSCharacterSet alphanumericCharacterSet];
BOOL valid = [[text stringByTrimmingCharactersInSet:alphaSet] isEqualToString:#""];
return valid;
}
Equivalent Swift 3 version of the answer provided by #user1113101
Though it's late to answer and there are other simple and great approaches, but this answer might be useful to someone.
This is simple and worked like a charm for me.
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
/// 1. replacementText is NOT empty means we are entering text or pasting text: perform the logic
/// 2. replacementText is empty means we are deleting text: return true
if text.characters.count > 0 {
var allowedCharacters = CharacterSet.alphanumerics
let unwantedStr = text.trimmingCharacters(in: allowedCharacters)
return unwantedStr.characters.count == 0
}
return true
}
Note: This will work for pasting strings into the text field as well. Pasted string will not be displayed in text field if it contains any unwanted characters.
// Add this in ViewDidLoad or any init method
NSCharacterSet *blockedCharacters = [[[NSCharacterSet alphanumericCharacterSet] invertedSet] retain];
then Set your textfield's delegate in nib file .
- (BOOL)textField:(UITextField *)field shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)characters
{
return ([characters rangeOfCharacterFromSet:blockedCharacters].location == NSNotFound);
}
Or there is another way in shouldChangeCharactersInRange method. You can check
{
NSString *stringPlace = #"[a-z A-Z]*";
NSPredicate *testPlace = [NSPredicate predicateWithFormat:#"SELF MATCHES %#", stringPlace];
BOOL matches = [testPlace evaluateWithObject:string];
if (!matches && string.length > 5)
{
return NO;
}
return YES;
}

UITextView, how do I remove the return key:linefeed?

I want to remove the linefeed that was entered in by the return key. But when I do the following it remove the last character from the text. Why?
- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text;
{
NSLog(#"%d %d %# %#",range.location,range.length, text, [textView text]);
if ( [text isEqualToString:#"\n"] ) {
NSString *s = [textView text];
s = [s substringToIndex:[s length] - 1]; // <------------
[tvText setText:[NSString stringWithFormat:#"%#\n>>",s]];
}
return YES;
}
I want the result to look like:
>>name
>>yoda
>> <---- cursor is moved to the right of ">>"
I think you can do something like this,
- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text;
{
NSLog(#"%d %d %# %#",range.location,range.length, text, [textView text]);
if ( [text isEqualToString:#"\n"] ) {
[tvText setText:[NSString stringWithFormat:#"%#\n>>",tvText.text]];
return NO;
}
return YES;
}
Or maybe after your reading string in line and put it to some substring:
string = [string stringByReplacingOccurrencesOfString:#"\n;" withString:#""];
shouldChangeTextInRange is part of the UITextViewDelegate, and is called before the new text is changed in the textView. Therefore, you could just do this:
- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text;
{
if ([text isEqualToString:#"\n"])
{
return NO;
}
return YES;
}
Use this for better solution as it won't allow user to post any blank message in any case.
//These for loops will remove the spaces and new line characters from start and end of the string
//&&//
NSMutableString *temp = [[NSMutableString alloc] initWithString:posttextview.text];
//Remove spaces and new line characters from start
for(int i = 0; i < yourtext.length; i++)
{
NSString *temp1 = [NSMutableString stringWithString:[temp substringWithRange:NSMakeRange(0,1)]];
if([temp1 isEqualToString:#"\n"] || [temp1 isEqualToString:#" "])
{
[temp deleteCharactersInRange:NSMakeRange(0,1)];
}
else
{
break;
}
}
yourtext.text = temp;
//Remove spaces and new line characters from end
for(int i = 0; i < yourtext.length; i++)
{
NSString *temp1 = [NSMutableString stringWithString:[temp substringWithRange:NSMakeRange(posttextview.text.length - 1,1)]];
if([temp1 isEqualToString:#"\n"] || [temp1 isEqualToString:#" "])
{
[temp deleteCharactersInRange:NSMakeRange(posttextview.text.length - 1,1)];
yourtext.text = temp;
}
else
{
break;
}
}
yourtext.text = temp;
//**//
The problem is that the time shouldChangeCharactersImRange is called, the new text is not actually changed yet (that's why it's not named didChangeCharactersInRange...). So in case you encounter a newline, don't trick with the substrings, just store/process the string the text view contains so far, and return NO.
First add the UITextViewDelegate in your .h file
#interface YourClass : UITextField <UITextFieldDelegate> {
}
then implement the delegate method
-(BOOL)shouldChangeCharactersInRange:replacementString: