can't read multiple words from uitextfield - objective-c

I am trying to read the name of a product which is entered by the user in a text field, and add it to a string later. This works fine whenever the user inputs only one word, but when the product is more than one word, the program crashes. When I run the debugger, the value of the NSString product is listed as "variable is not nsstring". This method is triggered when the user hits return after entering the product's name:
-(IBAction)textFieldReturn3:(id)sender
{
[sender resignFirstResponder];
product = inputtext.text;
}
and this is part of the method that is triggered when the user hits a twitter button two scenes later (my goal is to have a certain message displayed already when the user is deciding what to tweet) [and yes, choice is set to either 0 or 3]
- (IBAction)twitter:(id)sender
{
TWTweetComposeViewController *twitter = [[TWTweetComposeViewController alloc] init];
[fmt5 setNumberStyle:NSNumberFormatterDecimalStyle];
[fmt5 setMaximumFractionDigits:2];
[fmt5 setMinimumFractionDigits:2];
if (choice == #"0" || choice == #"3")
{
str = #"I just bought a new ";
str = [str stringByAppendingString:product]; // line where it crashes
str = [str stringByAppendingString:#" for $"];
str = [str stringByAppendingString:[fmt5 stringFromNumber:[NSNumber numberWithDouble:myDouble]]];
[twitter setInitialText:str];
}
How do I fix this problem? Thanks.

Plenty of problems. Let's start with the actual reason.
product = inputtext.text;
is not good. If you use it later, when your view has already been deallocated, its text property is no longer valid. You have to retain it to have a reference to it. So change the above line to something like this:
[product release];
product = [inputtext.text retain];
Also don't forget to initialize product to nil to avoid crashes related to the first release call.
Secondly, your comparison isn't good.
choice == #"0" || choice == #"3"
doesn't do what you think it does. Use
if ([choice isEqualToString:#"0"] || [choice isEqualToString:#"3"]) {
etc.
Edit: also a minor design point. Don't reinvent the wheel by appending portions of the text sequentially - that's extremely unreadable. Browsing through NSString's class reference, you would have quickly found the following method:
str = [NSString stringWithFormat:#"I just bought a new %# for $%.2lf", product, myDouble];

Related

Removing text from NSString between "#" and ":" while keeping the text after it

So I'm reading the most recent tweet from a twitter bot and assigning it to a string, but sometimes it tweets directly to users. Here's an example of what that might look like.
NSString tweet = #("#user hey heres my message: BLah blah with symbols!");
//part I want to keep is: " BLah blah with symbols!"
//or it could end up being
NSString tweet = #("#otheruser my msg is: Wow heres some more blah: and a second colon");
//part I want to keep is: " Wow heres some more blah: and a second colon"
I want to always remove the first part that talks to the user, while keeping the message on the end. There are too many different messages to use "stringByReplacingOccurrencesOfString"
I don't want to use "exlude-replies" from the twitter API because this bot is very popular and that would require fetching up to 100 since "count" applies before
any idea how I could do this? I think it has something to do with regular expressions, but I haven't ever used them before or been able to get one working how I want. I would really appreciate the help from anyone whos comfortable with regex
edit: also if a regex won't work for this case, id accept an explanation of the limitation preventing that :)
The easiest solution that I can think of is to create an NSMutable array, by using the NSString function componentsSeparatedBy:#":", and simply remove the first element.
NSMutableArray *tweetArray = [[NSMutableArray alloc] initWithArray: [tweet componentsSeparatedByString:#":"]];
[tweetArray removeObjectAtIndex:0];
Your issue with a colon appearing at random afterwards can be fixed by joining the pieces back together again.
tweet = [tweetArray componentsJoinedByString:#":"];
Edit: Fix an error pointed out by user 'maddy'
You'll have to stick this code in an if statement so that it does not execute in tweets that have a colon normally. You can use the fact that it always begins with #user however.
if ([tweet characterAtIndex:0] == '#' && [tweet characterAtIndex:1] != ' '){
NSMutableArray *tweetArray = [[NSMutableArray alloc] initWithArray: [tweet componentsSeparatedByString:#":"]];
[tweetArray removeObjectAtIndex:0];
tweet = [tweetArray componentsJoinedByString:#":"];
}
You can also use this.
NSString *myString = #"#username is me: by using this as sample text";
NSRange range = [myString rangeOfString:#":"];
NSString *newString= [myString substringFromIndex:range.location];
NSLog(#"New String is : %#", newString);

Sending WatchKit Voice recognition's text to another interface controller

I am having an issue sending dictated text to another interface controller.
Here is my code:
- (IBAction)voiceRecognition {
[self presentTextInputControllerWithSuggestions:nil allowedInputMode:WKTextInputModePlain completion:^(NSArray *results) {
NSLog(#"results: %#", results);
NSString *wordKey = [NSString stringWithFormat:#"%#",results];
NSDictionary *dict = #{#"kWord":wordKey};
[self pushControllerWithName:#"Dictionary" context:dict];
}];
}
Logs:
Watch Extension[3185:2835671] results: ( Hello )
Getting data from other Interface controller:
- (void)awakeWithContext:(id)context {
[super awakeWithContext:context];
NSDictionary *dict = (NSDictionary *)context;
[_word setText:dict[#"kWord"]];
NSLog(#"The Word is %#",[dict description]);
}
Logs:
Watch Extension[3185:2835671] The Word is {
kWord = "(\n Hello\n)";
}
Here is a screen shot that shows my problem:
The ( is supposed to show the word Hello. How can I fix this issue?
You used stringWithFormat to format an array as a string.
This took ["Hello"] and correctly converted it to the literal "(\n Hello\n)"
Because that string has a newline, it can't be displayed on a single line. Your Storyboard WKInterfaceLabel number of lines is likely set to 1, so it would only show the first line, which is (.
How can you fix this?
If you're only interested in the first word, use results.firstObject and pass that single word as the string value for your kWord key.
NSDictionary *dict = #{#"kWord": results.firstObject};
Otherwise, pass the entire array as the value, and have the destination interface controller handle the array of results as needed.
NSDictionary *dict = #{#"kWord": results};
You also may want to change the number of lines to show the entire dictation text, to handle the case where the text wouldn't fit on a single line.
Other options:
If you actually intended to send the dictated text as a single string of words, you can use
NSString *wordKey = [results componentsJoinedByString:#" "]

Making a simple quiz game. Stuck with the arrays part

I'm trying to make a game with 2 views, the first view has buttons which segues to another view. Depending on the segue identifier, it loads an image which the player has to guess.
I also have a an array which lists hints for the 4 images.
With the array, i made a button which shows the hints on the view, but the problem I have right now is that I don't know how to set the correct array to the image/puzzle.
A code that works right now is this:
if ([self.thePuzzle.name isEqual: #"lion"])
{
NSArray *hints = [lines[0] componentsSeparatedByString:#" "];
self.hintLabel.text = hints[0];
}
But after adding another line for the second image/puzzle, the app crashes.
2nd UPDATE: code that crashes
After entering this code
if ([self.thePuzzle.name isEqual: #"penguin"])
{
NSArray *hints = [lines[1] componentsSeparatedByString:#" "];
self.hintLabel.text = hints[1];
}
The hint button works for the 1st code, which has the lion picture, while the second part of the code, for the penguin pic, crashes when the Hint button is pressed.
3rd Update: Additional information
I made xcode access a file from the internet which contained the words for my array.
This is how i coded it.
[super viewDidLoad];
// Do any additional setup after loading the view.
self.imageView.image = [UIImage imageNamed:self.thePuzzle.imgFileName];
NSString *urlString = #"http://m.uploadedit.com/b032/1395295852132.txt";
NSString *contents = [TextFileManager readStringFromURL:urlString];
//parse contents
lines = [contents componentsSeparatedByString:#"\n"];
for( int i = 0; i < lines.count; i++)
{
NSString *line = lines[i];
NSLog(#"%d: %#", i, line);
}

How to do an on-item-changed for an NSPopUpButton?

I'm trying to implement a system that changes a label based on the state of an NSPopUpButton.
So far I've tried to do what's displayed in the code below, but whenever I run it, the code just jumps into the else clause, throwing an alert
- (IBAction)itemChanged:(id)sender {
if([typePopUp.stringValue isEqualToString: #"Price per character"]) {
_currency = [currencyField stringValue];
[additionalLabel setStringValue: _currency];
}
else if([typePopUp.stringValue isEqualToString: #"Percent saved"]) {
_currency = additionalLabel.stringValue = #"%";
}
else alert(#"Error", #"Please select a calculation type!");
}
So does anyone here know what to do to fix this?
#hamstergene is on the right track, but is comparing the title of the menu item rather than, say, the tag, which is wrong for the following reasons:
It means you cannot internationalize the app.
It introduces the possibility of spelling mistakes.
It's an inefficient comparison; comparing every character in a string takes way longer than comparing a single integer value.
Having said all that, NSPopUpButton makes it difficult to insert tags into the menu items, so you need to use the index of the selected item:
Assume you create the menu items using:
[typePopUp removeAllItems];
[typePopUp addItemsWithTitles: [NSArray arrayWithObjects: #"Choose one...", #"Price per character", #"Percent saved", nil]];
Then create an enum that matches the order of the titles in the array:
typedef enum {
ItemChooseOne,
ItemPricePerCharacter,
ItemPercentSaved
} ItemIndexes;
And then compare the selected item index, as follows:
- (IBAction)itemChanged:(id)sender {
NSInteger index = [(NSPopUpButton *)sender indexOfSelectedItem];
switch (index) {
case ItemChooseOne:
// something here
break;
case ItemPricePerCharacter:
_currency = [currencyField stringValue];
[additionalLabel setStringValue: _currency];
break;
case ItemPercentSaved:
_currency = #"%"; // See NOTE, below
additionalLabel.stringValue = #"%";
break;
default:
alert(#"Error", #"Please select a calculation type!");
}
}
NOTE the following line was incorrect in your code:
_currency = additionalLabel.stringValue = #"%";
Multiple assignment works because the result of x = y is y. This is not the case when a setter is involved. The corrected code is above.
EDIT This answer was heavily edited following more info from the OP.
To query the title of currently selected item in NSPopUpButton:
NSMenuItem* selectedItem = [typePopUp selectedItem];
NSString* selectedItemTitle = [selectedItem title];
if ([selectedItemTitle isEqualTo: ... ]) { ... }
Note that comparing UI strings is a very bad idea. A slightest change in UI will immediately break your code, and you are preventing future localization. You should assign numeric or object values to each item using -[NSMenuItem setTag:] or -[NSMenuItem setRepresentedObject:] and use them to identify items instead.

Is there a way to get Spell Check data from an NSString?

I'm writing a simple shift cipher iPhone app as a pet project, and one piece of functionality I'm currently designing is a "universal" decryption of an NSString, that returns an NSArray, all of NSStrings:
- (NSArray*) decryptString: (NSString*)ciphertext{
NSMutableArray* theDecryptions = [NSMutableArray arrayWithCapacity:ALPHABET];
for (int i = 0; i < ALPHABET; ++i) {
NSString* theNewPlainText = [self decryptString:ciphertext ForShift:i];
[theDecryptions insertObject:theNewPlainText
atIndex:i];
}
return theDecryptions;
}
I'd really like to pass this NSArray into another method that attempts to spell check each individual string within the array, and builds a new array that puts the strings with the fewest typo'd words at lower indicies, so they're displayed first. I'd like to use the system's dictionary like a text field would, so I can match against words that have been trained into the phone by its user.
My current guess is to split a given string up into words, then spell check each with NSSpellChecker's -checkSpellingOfString:StartingAt: and using the number of correct words to sort the Array. Is there an existing library method or well-accepted pattern that would help return such a value for a given string?
Well, I found a solution that works using UIKit/UITextChecker. It correctly finds the user's most preferred language dictionary, but I'm not sure if it includes learned words in the actual rangeOfMisspelledWords... method. If it doesn't, calling [UITextChecker hasLearnedWord] on currentWord inside the bottom if statement should be enough to find user-taught words.
As noted in the comments, it may be prudent to call rangeOfMisspelledWords with each of the top few languages in [UITextChecker availableLanguages], to help multilingual users.
-(void) checkForDefinedWords {
NSArray* words = [message componentsSeparatedByString:#" "];
NSInteger wordsFound = 0;
UITextChecker* checker = [[UITextChecker alloc] init];
//get the first language in the checker's memory- this is the user's
//preferred language.
//TODO: May want to search with every language (or top few) in the array
NSString* preferredLang = [[UITextChecker availableLanguages] objectAtIndex:0];
//for each word in the array, determine whether it is a valid word
for(NSString* currentWord in words){
NSRange range;
range = [checker rangeOfMisspelledWordInString:currentWord
range:NSMakeRange(0, [currentWord length])
startingAt:0
wrap:NO
language:preferredLang];
//if it is valid (no errors found), increment wordsFound
if (range.location == NSNotFound) {
//NSLog(#"%# %#", #"Valid Word found:", currentWord);
wordsFound++;
}
else {
//NSLog(#"%# %#", #"Invalid Word found:", currentWord);
}
}
//After all "words" have been searched, save wordsFound to validWordCount
[self setValidWordCount:wordsFound];
[checker release];
}