Backward with custom string - ios

I used a string array for emoticons like this:
NSArray *emoticons = #[#"[smile]",#"[cry]",#"[happy]" ...]
then in a UITextView displaying a string like this:
I'm so happy now [happy] now [smile]
When I click a backward or delete button, if the last word is in emoticons, I want a whole emoticon string be deleted, not the last one character only.
Any idea?

Try this,
NSString *string = self.textView.text;
__block NSString *deleteWord = nil;
__block NSRange rangeOfWord;
[string enumerateSubstringsInRange:NSMakeRange(0, self.textView.selectedRange.location + self.textView.selectedRange.length) options:NSStringEnumerationByWords | NSStringEnumerationReverse usingBlock:^(NSString *substring, NSRange subrange, NSRange enclosingRange, BOOL *stop) {
deleteWord = substring;
rangeOfWord = enclosingRange;
*stop = YES;
}];
if ([emoticons containsObject:deleteWord]) {
string = [string stringByReplacingCharactersInRange:rangeOfWord withString:#""];
self.textView.text = string;
self.textView.selectedRange = NSMakeRange(rangeOfWord.location, 0);
}

You might achieve something like this with the UITextViewDelegate method textView:shouldChangeTextInRange:replacementText: checking what is about to be deleted and remove the whole [emoticon] word.

I am giving you the idea that i used.
as you do not mentioned what you used as emoticons.
but for delete logic i think you will get idea from my this code.
if ([string isEqualToString:#""]) {
NSString *lastChar = [txthiddenTextField.text substringFromIndex: [txthiddenTextField.text length] - 1];
NSLog(#"Last char:%#",lastChar);
txthiddenTextField.text = [txthiddenTextField.text substringToIndex:[txthiddenTextField.text length] - 1];
NSString *strPlaceHolder;
strPlaceHolder = txthiddenTextField.text;
if([lastChar isEqualToString:#"]"])
{
int j = 1;
for (int i = [txthiddenTextField.text length]-1; i >=0; --i)
{
NSString *lastChar = [txthiddenTextField.text substringFromIndex: [txthiddenTextField.text length] - 1];
if([lastChar isEqualToString:#"["])
{
NSLog(#"%d",j);
txthiddenTextField.text = [txthiddenTextField.text substringToIndex:[txthiddenTextField.text length] - 1];
// NSLog(#"Processing character %#",strPlaceHolder);
break;
}
txthiddenTextField.text = [txthiddenTextField.text substringToIndex:[txthiddenTextField.text length] - 1];
j = j+1;
}
}
NSLog(#"My text fild value :%#",txthiddenTextField.text);
return YES;
}
So, from here you have to check if the closing bracket is coming or not.
if closing bracket will come then up to opening bracket you have to delete.
then whole emoticon will delete.
hope this helps....

Related

Find substring range of NSString with unicode characters

If I have a string like this.
NSString *string = #"😀1😀3😀5😀7😀"
To get a substring like #"3😀5" you have to account for the fact the smiley face character take two bytes.
NSString *substring = [string substringWithRange:NSMakeRange(5, 4)];
Is there a way to get the same substring by using the actual character index so NSMakeRange(3, 3) in this case?
Thanks to #Joe's link I was able to create a solution that works.
This still seems like a lot of work for just trying to create a substring at unicode character ranges for an NSString. Please post if you have a simpler solution.
#implementation NSString (UTF)
- (NSString *)substringWithRangeOfComposedCharacterSequences:(NSRange)range
{
NSUInteger codeUnit = 0;
NSRange result;
NSUInteger start = range.location;
NSUInteger i = 0;
while(i <= start)
{
result = [self rangeOfComposedCharacterSequenceAtIndex:codeUnit];
codeUnit += result.length;
i++;
}
NSRange substringRange;
substringRange.location = result.location;
NSUInteger end = range.location + range.length;
while(i <= end)
{
result = [self rangeOfComposedCharacterSequenceAtIndex:codeUnit];
codeUnit += result.length;
i++;
}
substringRange.length = result.location - substringRange.location;
return [self substringWithRange:substringRange];
}
#end
Example:
NSString *string = #"😀1😀3😀5😀7😀";
NSString *result = [string substringWithRangeOfComposedCharacterSequences:NSMakeRange(3, 3)];
NSLog(#"%#", result); // 3😀5
Make a swift extension of NSString and use new swift String struct. Has a beautifull String.Index that uses glyphs for counting characters and range selecting. Very usefull is cases like yours with emojis envolved

Check IF one String contains the same characters as another string

I am trying to write a function which will allow me to determine whether one NSString* contains the characters of another NSString*. As an example, refer to the below scenario:
NSString *s1 = #"going";
NSString *s2 = #"ievngcogdl";
So essentially when the comparison between these 2 strings occurs, it should return true as the first string s1 has the same characters of the second string s2. Could I use an NSCountedSet? I know that this class has a method containsObject:(id) although I don't think that will solve my problem. Is there any other ways in completing this function and provide me the required results?
I think this method could be rather slow, but I would still favour it over [NSString rangeOfCharacterFromSet:], which requires creating an NSCharacterSet object per comparison:
- (BOOL)string:(NSString *)string containsAllCharactersInString:(NSString *)charString {
NSUInteger stringLen = [string length];
NSUInteger charStringLen = [charString length];
for (NSUInteger i = 0; i < charStringLen; i++) {
unichar c = [charString characterAtIndex:i];
BOOL found = NO;
for (NSUInteger j = 0; j < stringLen && !found; j++)
found = [string characterAtIndex:j] == c;
if (!found)
return NO;
}
return YES;
}
This will work -
-(BOOL) string:(NSString *)string1 containsInputString:(NSString *)string2 {
// Build a set of characters in the string
NSCountedSet *string1Set = [[NSCountedSet alloc]init];
[string1 enumerateSubstringsInRange:NSMakeRange(0, string1.length)
options:NSStringEnumerationByComposedCharacterSequences
usingBlock:^(NSString *substring, NSRange substringRange, NSRange enclosingRange, BOOL *stop) {
[string1Set addObject:substring];
}];
// Now iterated over string 2, removing characters from the counted set as we go
for (int i=0;i<string2.length;i++) {
NSRange range = [string2 rangeOfComposedCharacterSequenceAtIndex:i];
NSString *substring = [string2 substringWithRange:range];
if ([string1Set countForObject:substring]> 0) {
[string1Set removeObject:substring];
}
else {
return NO;
}
}
return YES;
}
Regular Expressions are the best way to check this type of conditions and check this link once
Below I am adding the code for your solution, please check once
NSString *s1 = #"going"
NSString *s2 = #"ievngcogdl";
if ([self string:s1 containsSameCharacterofString:s2]) {
NSLog(#"YES");
}
- (BOOL)string:(NSString *)str containsSameCharacterofString:(NSString *)charString
{
if (charString.length >= str.length) {
NSError *error = nil;
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:[NSString stringWithFormat:#"^[%#]+$", charString] options:NSRegularExpressionCaseInsensitive error:&error];
NSRange textRange = NSMakeRange(0, str.length);
NSRange matchRange = [regex rangeOfFirstMatchInString:str options:NSMatchingReportProgress range:textRange];
return (matchRange.location != NSNotFound);
}
else {
return NO;
}
}
BOOL containsString = [#"Hello" containsString:#"llo"];
if (containsString) {
// Do Stuff
}

Occurance of character after specific index

Is there any way to achieve what JAVA function int indexOf(int ch, int fromIndex) do.
Let me explain it:
NSString *steSample = #"This is sample test string";
Now i want to get the index of i but after 2nd index. How can I achieve this.
Thanks in advance
the regex way is too complicated for me :)
can't we just trim it and then look for it?
that'd be 3 lines...
wrapped in a category:
#import <Foundation/Foundation.h>
#interface NSString (Extend)
-(NSUInteger)indexOfSubstring:(NSString*)needle afterIndex:(NSUInteger)index;
#end
#implementation NSString (Extend)
-(NSUInteger)indexOfSubstring:(NSString*)needle afterIndex:(NSUInteger)index {
id str = [self substringFromIndex:index];
NSUInteger i = [str rangeOfString:needle].location;
return i==NSNotFound ? i : i+index;
}
#end
Demo usage:
int main(int argc, char *argv[]) {
#autoreleasepool {
id str = #"#asd#asd";
NSUInteger index = [str indexOfSubstring:#"#" afterIndex:2];
NSLog(#"index of # is: %d", index);
}
}
I'd do something like this:
NSString *_sample = #"This is sample test string";
NSError *_error;
NSRegularExpression *_regExp = [NSRegularExpression regularExpressionWithPattern:#"i" options:NSRegularExpressionCaseInsensitive error:&_error];
NSArray *_matches = [_regExp matchesInString:_sample options:NSMatchingReportCompletion range:NSMakeRange(0, _sample.length)];
[_matches enumerateObjectsUsingBlock:^(NSTextCheckingResult * result, NSUInteger idx, BOOL *stop) {
if (idx == 0) {
NSLog(#"ignoring first occurance...");
} else {
NSLog(#"occurance's index : %d, character's index in string : %d", idx, result.range.location); // that line is simplified for your problem
}
}];
NOTE: you can rearrange the actual if statement, it currently 'skips' the first occurance and prints the rest – but it can be customised for your further wish.
my console shows something like this:
ignoring first occurance...
occurance's index : 1, character's index in string : 5
occurance's index : 2, character's index in string : 23
NSString *steSample = #"This is sample test string";
NSUInteger count = 0, length = [steSample length];
NSRange range = NSMakeRange(0, length);
while(range.location != NSNotFound)
{
range = [steSample rangeOfString: #"i" options:0 range:range];
if(range.location != NSNotFound)
{
range = NSMakeRange(range.location + range.length, length - (range.location + range.length));
count++;
if (count == 2)
{
NSLog(#"%d", range.location); // print 6 which is location of second 'i'
}
}
}

NSString to treat "regular english alphabets" and characters like emoji or japanese uniformly

There is a textView in which I can enter Characters. characters can be a,b,c,d etc or a smiley face added using emoji keyboard.
-(void)textFieldDidEndEditing:(UITextField *)textField{
NSLog(#"len:%lu",textField.length);
NSLog(#"char:%c",[textField.text characterAtIndex:0]);
}
Currently , The above function gives following outputs
if textField.text = #"qq"
len:2
char:q
if textField.text = #"😄q"
len:3
char:=
What I need is
if textField.text = #"qq"
len:2
char:q
if textField.text = #"😄q"
len:2
char:😄
Any clue how to do this ?
Since Apple screwed up emoji (actually Unicode planes above 0) this becomes difficult. It seems it is necessary to enumerate through the composed character to get the actual length.
Note: The NSString method length does not return the number of characters but the number of code units (not characters) in unichars. See NSString and Unicode - Strings - objc.io issue #9.
Example code:
NSString *text = #"qqq😄rrr";
int maxCharacters = 4;
__block NSInteger unicharCount = 0;
__block NSInteger charCount = 0;
[text enumerateSubstringsInRange:NSMakeRange(0, text.length)
options:NSStringEnumerationByComposedCharacterSequences
usingBlock:^(NSString *substring, NSRange substringRange, NSRange enclosingRange, BOOL *stop) {
unicharCount += substringRange.length;
if (++charCount >= maxCharacters)
*stop = YES;
}];
NSString *textStart = [text substringToIndex: unicharCount];
NSLog(#"textStart: '%#'", textStart);
textStart: 'qqq😄'
An alternative approach is to use utf32 encoding:
int byteCount = maxCharacters*4; // 4 utf32 characters
char buffer[byteCount];
NSUInteger usedBufferCount;
[text getBytes:buffer maxLength:byteCount usedLength:&usedBufferCount encoding:NSUTF32StringEncoding options:0 range:NSMakeRange(0, text.length) remainingRange:NULL];
NSString * textStart = [[NSString alloc] initWithBytes:buffer length:usedBufferCount encoding:NSUTF32LittleEndianStringEncoding];
There is some rational for this in Session 128 - Advance Text Processing from 2011 WWDC.
This is what i did to cut a string with emoji characters
+(NSUInteger)unicodeLength:(NSString*)string{
return [string lengthOfBytesUsingEncoding:NSUTF32StringEncoding]/4;
}
+(NSString*)unicodeString:(NSString*)string toLenght:(NSUInteger)len{
if (len >= string.length){
return string;
}
NSInteger charposition = 0;
for (int i = 0; i < len; i++){
NSInteger remainingChars = string.length-charposition;
if (remainingChars >= 2){
NSString* s = [string substringWithRange:NSMakeRange(charposition,2)];
if ([self unicodeLength:s] == 1){
charposition++;
}
}
charposition++;
}
return [string substringToIndex:charposition];
}

Finding word in NSString and checking before and after character this word?

How to find word in NSString and check characters before and after this word?
"This pattern has two parts separated by the"
How to find tern and how to check the character before and after
Before word character:"t"
After word character:" "
You can use NSScanner to get indexes of these two characters.
Example:
NSString *string = #"tern";
NSScanner *scanner = [[NSScanner alloc] initWithString:#"This pattern has two parts separated by the"];
[scanner scanUpToString:string intoString:nil];
NSUInteger indexOfChar1 = scanner.scanLocation - 1;
NSUInteger indexOfChar2 = scanner.scanLocation + string.length;
You can also use a rangeOfString method:
Example:
NSRange range = [sourceString rangeOfString:stringToLookFor];
NSUInteger indexOfChar1 = range.location - 1;
NSUInteger indexOfChar2 = range.location +range.length + 1;
Then, when you have indexes, getting the characters is easy:
NSString *firstCharacter = [sourceString substringWithRange:NSMakeRange(indexOfChar1, 1)];
NSString *secondCharacter = [sourceString substringWithRange:NSMakeRange(indexOfChar2, 1)];
Hope this helps.
Here is an implementation using Regular Expressions
NSString *testString= #"This pattern has two parts separated by the";
NSString *regexString = #"(.)(tern)(.)";
NSRegularExpression* exp = [NSRegularExpression
regularExpressionWithPattern:regexString
options:NSRegularExpressionSearch error:&error];
if (error) {
NSLog(#"%#", error);
} else {
NSTextCheckingResult* result = [exp firstMatchInString:testString options:0 range:NSMakeRange(0, [testString length] ) ];
if (result) {
NSRange groupOne = [result rangeAtIndex:1]; // 0 is the WHOLE string.
NSRange groupTwo = [result rangeAtIndex:2];
NSRange groupThree = [result rangeAtIndex:3];
NSLog(#"[%#][%#][%#]",
[testString substringWithRange:groupOne],
[testString substringWithRange:groupTwo],
[testString substringWithRange:groupThree] );
}
}
Results:
[t][tern][ ]
Its better to get pre and post character in NSString to avoid handling of unicode characters.
NSString * testString = #"This pattern has two parts separated by the";
NSString * preString;
NSString * postString;
NSUInteger maxRange;
NSRange range = [testString rangeOfString:#"tern"];
if(range.location == NSNotFound){
NSLog(#"Not found");
return;
}
if (range.location==0) {
preString=nil;
}
else{
preString = [testString substringWithRange:NSMakeRange(range.location-1,1)];
}
maxRange = NSMaxRange(range);
if ( maxRange >=testString.length ) {
postString = nil;
}
else{
postString = [testString substringWithRange:NSMakeRange(range.location+range.length, 1)];
}

Resources