extrapolate word from TextView by location - objective-c

I have a TextView and I need to insert a word in a string from the location. Use this method to derive the location
NSInteger location = textView.selectedRange.location;
But I do not know how to put this into a string.
Thanks a lot

Is it what you looking for?
NSInteger location = textView.selectedRange.location;
UITextView yourTxtView;
yourTxtView.text = [NSString stringWithFormat:#"%d",location];

If you want to replace the selection with a word, the simplest would be something like this:
NSRange range = textView.selectedRange;
if (range.location != NSNotFound) {
NSString *replacement = #"some word";
textView.text = [textView.text stringByReplacingCharactersInRange:range
withString:replacement];
}
For an empty selection, this would insert the text at the caret position, if text is selected, it would be replaced. On iOS 5, using the methods in the UITextInput protocol would probably be more efficient, but also more cumbersome to use.

Related

NSTextStorage - How to find return chars (new line) in a text

In an NSTextStorage I insert time strings at the current pointer location like this :
NSMutableAttributedString* attrString = [[NSMutableAttributedString alloc] initWithString: #"00:00:00"];
[attrString autorelease];
int pos = [self selectedRange].location;
[[self textStorage] insertAttributedString: attrString atIndex:pos];
So far so good, it works perfect. But now I want to position the pointer at the beginning of the next line. Obviously this is right after the next return char.
Now how to find the next return char in textStorage and position the pointer there ?
I have not found any hint in the web for this task. Please help ...
You won't find a method to set the cursor (more precise: selection with zero length) in NSTextStorage's API, because it is a storage, having no selection. The selection is a property of the text view. This is the result of MVC. Simply do a check: You can have many text views displaying the same text. Obviously each one needs its own selection.
What you have to do is to get the position of the next paragraph (this is better than searching for \n) and set this as a selection for the text view.
NSMutableAttributedString* attrString = [[NSMutableAttributedString alloc] initWithString: #"00:00:00"];
[attrString autorelease]; // You should *really* use ARC
int pos = [self selectedRange].location;
[[self textStorage] insertAttributedString: attrString atIndex:pos];
// Get the next paragraph
NSString *text = self.textStorage.string;
NSRange restOfStringRange = NSMakeRange( pos, [text length]-pos );
NSUInteger nextParagraphIndex;
// You have to look to the start of the next paragraph
[text getParagraphStart:&nextParagraphIndex end:NULL contentsEnd:NULL forRange:restOfStringRange];
NSTextView *view = self.textView; // Or where ever you get it from
view.selectedRange = NSMakeRange(nextParagraphIndex, 0);
Typed in Safari, not tested, just to show the basic approach.

Comparing string to a character of another string?

Here's my program so far. My intention is to have it so the if statement compares the letter in the string letterGuessed to a character in the string userInputPhraseString. Here's what I have. While coding in xCode, I get an "expected '['"error. I have no idea why.
NSString *letterGuessed = userInputGuessedLetter.text;
NSString *userInputPhraseString = userInputPhraseString.text;
int loopCounter = 0;
int stringLength = userInputPhraseString.length;
while (loopCounter < stringLength){
if (guessedLetter isEqualToString:[userInputPhraseString characterAtIndex:loopIndexTwo])
{
//if statement true
}
loopCounter++;
}
You are missing enclosing square brackets on this line:
if (guessedLetter isEqualToString:[userInputPhraseString characterAtIndex:loopIndexTwo])
It should be:
if ([guessedLetter isEqualToString:[userInputPhraseString characterAtIndex:loopIndexTwo]])
Edit that won’t fix your problem, though, because characterAtIndex: returns a unichar, not an NSString.
It's not clear what you are trying to do.. But I suppose that letterGuessed has one character... And that userInputPhraseString has many characters. So you want to know if letterGuessed is inside userInputPhraseString correct?
This is one solution without loops involved.. I replaced the input with fixed values for testing and tested the code.. It works.
NSString *letterGuessed = #"A"; //Change to your inputs
NSString *userInputPhraseString = #"BBBA"; //Since it has A it will be true in the test
NSCharacterSet *cset = [NSCharacterSet characterSetWithCharactersInString:letterGuessed];
NSRange range = [userInputPhraseString rangeOfCharacterFromSet:cset];
if (range.location != NSNotFound) { //Does letterGuessed is in UserInputPhraseString?
NSLog(#"YES"); //userInput Does contain A...
} else {
NSLog(#"NO");
}
In regards to your code... I fixed a couple of errors, first you are trying to get a UniChar (Integer) value for the character and want to compare it to a NSString which is an Object. Also fixed a couple of issues with syntax you had and used the right approach which is to return a range of characters. Again for doing what you want to accomplish the example above is the best approach I know, but for the sake of learning, here is your code fixed.
NSString *letterGuessed = #"A"; //Change to your inputs
NSString *userInputPhraseString = #"BBBA"; //Since it has A it will be true in the test
NSInteger loopCounter = 0; //Use NSInteger instead of int.
NSInteger stringLength = userInputPhraseString.length;
BOOL foundChar = NO; //Just for the sake of returning NOT FOUND in NSLOG
while (loopCounter < stringLength){
//Here we will get a letter for each iteration.
NSString *scannedLetter = [userInputPhraseString substringWithRange:NSMakeRange(loopCounter, 1)]; // Removed loopCounterTwo
if ([scannedLetter isEqualToString:letterGuessed])
{
NSLog(#"FOUND CHARACTER");
foundChar = YES;
}
loopCounter++;
}
if (!foundChar) NSLog(#"NOT FOUND");
NSRange holds the position, length.. So we move to a new position on every iteration and then get 1 character.
Also if this approach is what you want, I would strongly suggest a for-loop.

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!
}
}

Truncate a string

I have a NSTableView that shows the path of files in one column. When the user resizes the tableview I want the pathname (e.g. /Users/name/testfile.m) to be resized, but I want the end of the pathname (e.g. ...name/testfile.m) to be visible and not the start (e.g. /Users/test/te...) of the path as happens by default. I wrote a function that successfully does what I want to do, but the tableview flickers while redrawing as the user scales the tableview. I think there must be a better, more elegant algorithm for doing this, but I have looked into the documentation for NSString and on Stackoverflow and I cant find anything that gives a better solution. If anyone has a more elegant solution to this problem that would be greatly appreciated. Thanks! Cheers, Trond
My current function:
-(NSString *) truncateString:(NSString *) myString withFontSize:(int) myFontSize withMaxWidth:(NSInteger) maxWidth
{
// Get the width of the current string for a given font
NSFont *font = [NSFont systemFontOfSize:myFontSize];
CGSize textSize = NSSizeToCGSize([myString sizeWithAttributes:[NSDictionary dictionaryWithObject:font forKey: NSFontAttributeName]]);
NSInteger lenURL =(int)textSize.width;
// Prepare for new truncated string
NSString *myStringShort;
NSMutableString *truncatedString = [[myString mutableCopy] autorelease];
// If the available width is smaller than the string, start truncating from first character
if (lenURL > maxWidth)
{
// Get range for first character in string
NSRange range = {0, 1};
while ([truncatedString sizeWithAttributes:[NSDictionary dictionaryWithObject:font forKey: NSFontAttributeName]].width > MAX(TKstringPad,maxWidth))
{
// Delete character at start of string
[truncatedString deleteCharactersInRange:range];
}
myStringShort = [NSString stringWithFormat:#"...%#",truncatedString];
}
else
{
myStringShort=myString;
}
return myStringShort;
}
The typical approach would be simply:
[tableViewCell setLineBreakMode:NSLineBreakByTruncatingHead];
As Dondragmer noted, this property may also be set in Xcode's NIB editor.

capitalizedString doesn't capitalize correctly words starting with numbers?

I'm using the NSString method [myString capitalizedString], to capitalize all words of my string.
However capitalization doesn't work very well for words starting with numbers.
i.e. 2nd chance
becomes
2Nd Chance
Even if n is not the first letter of the word.
thanks
You have to roll your own solution to this problem. The Apple docs state that you may not get the specified behavior using that function for multi-word strings and for strings with special characters. Here's a pretty crude solution
NSString *text = #"2nd place is nothing";
// break the string into words by separating on spaces.
NSArray *words = [text componentsSeparatedByString:#" "];
// create a new array to hold the capitalized versions.
NSMutableArray *newWords = [[NSMutableArray alloc]init];
// we want to ignore words starting with numbers.
// This class helps us to determine if a string is a number.
NSNumberFormatter *num = [[NSNumberFormatter alloc]init];
for (NSString *item in words) {
NSString *word = item;
// if the first letter of the word is not a number (numberFromString returns nil)
if ([num numberFromString:[item substringWithRange:NSMakeRange(0, 1)]] == nil) {
word = [item capitalizedString]; // capitalize that word.
}
// if it is a number, don't change the word (this is implied).
[newWords addObject:word]; // add the word to the new list.
}
NSLog(#"%#", [newWords description]);
Unfortunately this seems to be the general behaviour of capitalizedString.
Perhaps a not so nice workaround / hack would be to replace each number with a string before the transformation, and then change it back afterwards.
So, "2nd chance" -> "xyznd chance" -> "Xyznd Chance" -> "2nd Chance"