I am having a IBAction for 4 buttons which represent 4 available answers and each time I press a button I read the sender ID, then it figures out if answer is correct and so on.
Now I have a special case, when another button (nothing in common with those 4 buttons) is pressed, it waits for two of these answers, checks if one of them is correct( i know how to do that) and then the program continues running.
So for now, I have :
- (IBAction)answerButtonPressed:(id)sender {
NSString* answer= [[sender titleLabel] text];
//checks if answer key is equal to a1 ( which is always the correct answer )
if([[currentQuestionUsefulVariable valueForKey:#"a1"] isEqualToString:answer] ){
correctQuestionsCount1 ++;
}
//answer is wrong
else{
wrongQuestionsCount1 ++;
}
}
As you see I store the answer string in a variable called answer
And again - All I need is to store two answers and check the two of them when this special button is pressed. I will of course put a boolean variable to indicate when it is pressed and it will do the work.
EDIT:
The two answer thing is when I press a specific joker button and it gives the advantage to the user to chose two of four available answers. This is why I need to do that. For any other cases I need only one answer at a time.
Any ideas ?
Well you're going to need an instance variable, the value of which persists throughout the lifetime of the object, and perhaps using a mutable array of the answers is the way forward:
#interface MyViewController ()
{
NSMutableArray *_correctAnswers;
}
It must be initialised in viewDidLoad (other options are available):
-(void)viewDidLoad {
[super viewDidLoad];
_correctAnswers = [NSMutableArray new];
}
and then start collecting correct answers into this array:
- (IBAction)answerButtonPressed:(id)sender {
NSString* answer= [[sender titleLabel] text];
//checks if answer key is equal to a1 ( which is always the correct answer )
if([[currentQuestionUsefulVariable valueForKey:#"a1"] isEqualToString:answer] ){
[_correctAnswers addObject:answer];
}
//answer is wrong
else{
wrongQuestionsCount1 ++;
}
if ([_correctAnswers count] == 2) {
// Do something? It's not exactly clear what you want to do
// when 2 correct answers have been given.
}
}
Note: you can dump correctQuestionsCount1 as that will be [_correctAnswers count] now. Also you will need to reset the array at some point.
Note 2: You could also start collecting incorrect answers as well, for some analysis or perhaps to disable that button so the user cannot repeatedly answer the question wrong.
Related
Beginner programmer trying to understand why I cannot accomplish this simple task. I know it is most likely a very simple solution but hoping that someone will explain the WHY.
I have a screen where users can input emails into a textfield. The idea is that if upon entering the e-mail, if it is not already in the stored emails, it will prompt the user to enter the new email/contact into the store. Since I check if the e-mail is valid BEFORE popping up the screen for contact creation, I'd like to take the text entered and put it directly into the "Email" field on the new contact creation page and not allow editing. I've tried numerous methods but CANNOT get the email to show up in the text field. Can someone explain why this is?
Code from initial VC where users enter their emails. If the email does not exist in the store, this code creating the contact creator page is fired:
//I created this custom initializer since setting the text field (as I did below) would not work
ContactCreationViewController *contactCreationVC = [[ContactCreationViewController alloc] initWithEmail:trimmedText];
contactCreationVC.delegate = self;
//initially I tried setting the text here but it did not work, I now understand why
//[contactCreationVC.emailField setText:#"asdsadasd"];
[contactCreationVC setModalTransitionStyle:UIModalTransitionStyleCrossDissolve];
[contactCreationVC setModalPresentationStyle:UIModalPresentationOverCurrentContext];
[self presentViewController:contactCreationVC animated:YES completion:nil];
This is the code for the actual ContactCreatorVC:
-(instancetype) initWithEmail:(NSString*)email{
self = [super init];
//tried setting email here which works as I check with breakpoints
[self setEmail:email];
//self.email = email here when I check
return self;
}
....
- (nonnull UITableViewCell *)tableView:(nonnull UITableView *)tableView cellForRowAtIndexPath:(nonnull NSIndexPath *)indexPath {
switch (indexPath.row) {
....
case 3: {
ContactCreationTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:contactCreationCellIdentifier];
cell.titleLabel.text = #"E-Mail";
cell.userEntryTextfield.tag = 3;
cell.userEntryTextfield.delegate = self;
cell.userEntryTextfield.keyboardType = UIKeyboardTypeEmailAddress;
//I try setting the email here but self.email = nil (don't understand why)
cell.userEntryTextfield.text = [NSString stringWithFormat:#"%#", self.email];
cell.userEntryTextfield.userInteractionEnabled = NO;
cell.userEntryTextfield.autocapitalizationType = UITextAutocapitalizationTypeNone;
self.emailField = cell.userEntryTextfield;
return cell;
}
.....
}
I feel stupid for even asking such a simple question but I clearly am not understanding what is going on behind the scenes cause I've tried everything. Can anyone explain what is going on and suggest the ideal solution from a best practice standpoint?
EDIT: I guess my question could be more concise...it basically boils down to: why when I set self.email in the init does it not stick when I access self.email in the cellForRow method (it becomes nil)?
(the textfield is in a cell in a tableview)
The problem is this line:
[contactCreationVC.emailField setText:#"asdsadasd"];
You have only just created contactCreationVC. Its view has not loaded, so emailField is nil (easily demonstrated by some rudimentary logging). A message to nil does nothing.
The correct approach is: never touch another view controller's outlets. Set a property of the other view controller and let it deal with its own outlets. In this case, the other view controller would need to use the property to set the emailField text in its viewDidLoad.
As to why that approach doesn't work for you, there isn't enough info to answer it as far as I can tell. If you can prove that things go in this order:
A ContactCreatorVC init is called with an actual email value, and the property is set.
The property has a strong (retain) policy so the value is retained.
The very same ContactCreatorVC instance now has its cellForRowAtIndexPath called, and that moment the property is nil.
If you can prove all that, then the only logical conclusion is that meanwhile some other code has come along and set the property to nil.
I have a UIViewController, and in that I have an array of questions that I pull from a sqlite3 query. I am then using a for loop to iterate through each question in the array to change the UILabel.text to display the question on the screen. This is actually working for the first question in the array!
I then have four buttons for answers. I want to make it so if one of the buttons is pressed, the answer is saved and the next question in the loop updates the UILabel.text.
The four answers never change as it is more of a survey than answers, so one of the answers is "I agree" or "disagree", so the button text never changes.
Is this possible?
I have been on here and Google to find a way to link the button pressed with completing each iteration of the loop without any luck.
Why are you iterating through questions and changing UILabel's text? Shouldn't be it changed only on tapping one of the survey buttons?
If I got you correctly, you should do following:
1) Declare three properties in your controller: NSArray *questions, NSMutabelArray *answers, NSInteger currentIndex;
2) Init/alloc them in viewDidLoad (except currentIndex, of course, set it to 0).
3) Fill up questions array with your question strings.
4) Set text to UILabel, label.text = questions[currentIndex];
5) create IBAction method and link it to all survey buttons.
6) in IBAction method, insert button's title to answers array and show next question.
- (void)viewDidLoad {
[super viewDidLoad];
self.questions = {your questions array};
self.answers = [[NSMutableArray alloc] init];
self.currentIndex = 0;
}
- (IBAction)btnClicked:(id)sender {
UIButton *btn = (UIButton *)sender;
NSString *title = btn.titleLabel.text;
[self.answers addObject:title];
currentIndex++;
label.text = questions[currentIndex];
}
I hope you will understand the code.
In short, yes this is possible.
You'll first want to keep track of the question that your user is currently on. You can do this by storing an index in an instance variable or, if you plan on allowing the user to open the app and start from where they left off, you can use NSUserDefaults, which writes to disk and will persist.
// In the interface of your .m file
int questionIndex;
// In viewDidLoad of your controller, however this will start for index 0, the beginning of your questions array
questionIndex = 0
By storing the index in NSUserDefaults, you can grab it in ViewDidLoad, and start from where the user last left off:
[[NSUserDefaults standardUserDefaults] setObject:[NSNumber numberWithInt:questionIndex] forKey:#"questionIndex"];
To store your answer, you could add a method for your buttons called answerTapped:,
- (void)answerTapped:(UIButton *)answerButton
{
// Grab the answer from the text within the label of the button
// NOTE: This assume your button text is the answer that you want saved
NSString *answer = answerButton.titleLabel.text;
// You can use your questionIndex, to store which question this answer was for and you can then take the answer and store it in sqlite or where you prefer...
}
You can add this method to your buttons like so
[answerButton addTarget:self action:#selector(answerTapped:) forControlEvents:UIControlEventTouchUpInside];
You could then write a method to increment questionIndex now that an answer button has been pressed.
- (void)incrementQuestionIndex
{
// Increment index
questionIndex += 1;
// Update and save value in UserDefaults
[[NSUserDefaults standardUserDefaults] setObject:[NSNumber numberWithInt:questionIndex] forKey:#"questionIndex"];
}
You could then call a separate, final method to update the question label.
- (void)updateQuestionLabel
{
// Grab the question using the index (omit this line and go straight to the next if storing the index in an iVar)
questionIndex = [[[NSUserDefaults standardUserDefaults] objectForKey:#"questionIndex"] integerValue];
// Grab the question using the index. Assumes you have a questions array storing your questions from sqlite.
NSString *question = [questions objectAtIndex:questionIndex];
// Update the question UILabel
[questionLabel setText:question];
}
I'm a beginner developer for iOS. I'm using objective c to develop an app. I want a button to be able to read a string from an array. What I mean by this is NOT to set the string as what the button displays. I want it to be able to get the string so I can use AVSpeechSynthesizser to read the string aloud. Thanks in advance
You don't provide any code or details about your problem.
I have to make assumption that you just want to read something from array while button is tapped.
Either using storyboard to create the button object and its handler or manually add the handler.
Let's say you have the button object named 'exampleButton', if you choose manually add the handler,
[exampleButton addTarget:self action:#selector(buttonTapped:) forControlEvents:UIControlEventTouchUpInside];
Let's say your array name is exampleArray, and you want to access the first element.
EDIT:
use firstObject instead objectAtIndex:0 since the latter one will crash the app if the array is empty.
- (IBAction)buttonTapped:(id)sender {
// becareful, if the array is empty, firstObject will return nil.
// If you use [exampleArray objectAtIndex:0], it will crash
id obj = [exampleArray firstObject];
if ([obj isKindOfClass:[NSString class]]) {
NSLog(#"%#", obj); // now you have the string object.
}
}
You have to learn more if you still cannot get yourself started with above code.
I currently have an array of buttons being generated which works great. Outside of this on touch up inside each button calls on a -(void)onTouch function where some math is done to determine an action. This all works great except I would like to store a history of the pressed buttons. I've tried many different ways to create an NSMutableArray and store the values of the pressed buttons, but because I can only declare the array within the -onTouch action, every time a button is pressed the array is reset so it never remembers more than one move. If I try to declare the array in my header and synthesize it outside I either get the error that nsmutable array is not a compile time thinger or it doesn't store anything (log output is "(null)". Can someone paste in some code on how to declare an array that can store and append the uibutton tags outside of where the uibutton press event happens? I'll post code later tonight if this isn't clear.
Cheers
You need to not only declare the array, but also initialise it. If you don't initialise, you won't necessarily get a warning, but you will get lots of nil data.
You only want to initialise the array once (as you have noticed) so viewDidLoad is a good place to do it. Another good place is in a custom accessor...
- (NSMutableArray*)historyArray
{
if (!_historyArray) {
_historyArray = [[NSMutableArray alloc] init];
}
return _historyArray;
}
Now the first time you try to [self.historyArray addObject:sender], the accessor will note the absence of a historyArray, create one and return it. Next time round it won't be recreated as it already exits.
#property(nonatomic,retain)NSMutableArray *tapCollection;
-(void)viewDidLoad{
self.tapCollection = [[NSMutableArray alloc] init];
}
-(void)onTouch:(id)sender{
UIButton *btnTapped = (UIButton *)sender;
[self.tapCollection addObject:[NSNumber numberWithInt:btn.tag]];
}
I'm trying to determine the user language in iOS. StackOverflow has several answers on this topic which has greatly helped me out, such as this one: Getting current device language in iOS?
I can successfully retrieve the value I'm looking for in NSLog (i.e. "en" or "de") but every time I question this with an if/then statement it doesn't appear to work. I have this in my viewDidLoad for testing:
- (void)viewDidLoad
{
[super viewDidLoad];
NSString *myLanguage = [[NSLocale preferredLanguages] objectAtIndex:0];
NSLog(#"The current languague is %#.", myLanguage);
if (myLanguage == #"de") {
self.myLabel.text = #"German";
} else if (myLanguage == #"en") {
self.myLabel.text = #"English";
} else {
self.myLabel.text = #"didn't work";
}
}
No matter if the device is set to English or German only the last else statement is displayed. NSLog however correctly displays either en or de.
What am I doing wrong?
NSString comparison is done with isEqualToString: method. In your code you are comparing two different NSString objects, while instead you have to compare the contents of each one of them.
If you have two objects of any kind, they are always different, even if all their members have the same values. That's why methods like this exist, to compare objects based on their members.
Replace:
if (myLanguage == #"de")
with
if ([myLanguage isEqualToString:#"de"])
and the same for the else ifs in your code.