Prevent choosing of certain objects in NSMutableArray? - ios

I am kind of in a pickle here since I have tried many things to accomplish this and have failed.
What I want to do: I have a NSString in my app which is the answer to a question to a round in a quiz. My answers are stored in a NSMutableArray. These answers are chosen at random. I have 4 buttons in my app that are options (possible answers) for the round. I don't want to have multiple answers!
What I have tried:
I have tried deleting the answer after storing it in one of the buttons so that it couldn't be chosen again but it ended up causing a crash since I was trying to delete a object in the array while using fast enumeration (for loop).
I have tried just detecting if the button title was equal to to the answer after setting the correct answer to a specific button but that didn't work for some odd reason (no crash). There will still multiple buttons with the same answer
What I need help with: How should I stop the answer from being in multiple buttons so that the quiz isn't showing the obvious answer?
What should I do instead?
Thanks!
Edit1: So I pretty much made a NSArray of my four UIButtons.
I put the answer into a random UIButton like this:
NSInteger chosen = (arc4random() % 4);
UIButton *randButton = (UIButton *)[buttonArray objectAtIndex:chosen];
[randButton setTitle:imageName forState:UIControlStateNormal];
Then I title the other buttons like so, my buttons that do not have the answer have a title of nothing so I do this:
- (void)titleButtons {
for (UIButton *buttons in buttonArray) {
if ([[buttons titleForState:UIControlStateNormal] == nil]) {
UIButton *button = buttons;
NSString *superheroString = (NSString*)[superheroArray objectAtIndex:(arc4random() % [superheroArray count])];
[button setTitle:superheroString forState:UIControlStateNormal];
[self checkTitles:button];
}
}
Then the checkTitle method looks like this, this is the method where I try to make sure where 2 buttons do not have the same answer as the imageName (answer):
- (void)checkTitles:(UIButton*)button {
if ([[button titleForState:UIControlStateNormal] isEqualToString:imageName]) {
//Duplicate Answer so re-title button
NSString *newTitle = [superheroArray objectAtIndex:(arc4random() % [superheroArray count])];
[button setTitle:newTitle forState:UIControlStateNormal];
//Call same method again to see if new title is still same answer as pic to avoid same answers
[self checkTitles:button];
}
}
}

If you have an NSArray with all your answers, and you want 1 correct answer and 3 different wrong answers, you can do the following:
Decide in which button do you want the right answer. (randomize it)
Get a random answer from your array and store the index of that answer in a temporary array.
Get another random answer and make sure you are not picking an answer with the same index as the one you have in your temp array, (and again, store the new index in the temporary array)

Don't compare strings using ==. This compares pointer addresses. Use isEqualToString: instead:
if (stringA == stringB)
Usually won't work (will for some constant strings)
if ([stringA isEqualToString:stringB])
Will always work.

Related

Loop in a variable of type IBOutlet

I have twelve text fields as you can see below:
IBOutlet UITextField *ce_1;
IBOutlet UITextField *ce_2;
IBOutlet UITextField *ce_3;
....
IBOutlet UITextField *ce_12;
All I have to do is to set an existing object in an array in each of the variables that are responsible for the text fields, I'm currently doing as follows:
ce_1.text = myArray[1];
ce_2.text = myArray[2];
ce_3.text = myArray[3];
....
ce_12.text = myArray[12];
Not to be writing a lot, I thought I'd put this in an automated way within a loop as follows:
for(i=1;i<13;i++){
ce_[i].text = myArray[i];
}
But this command does not work the way I expected, so I would like your help to try to solve my idea and put it into practice, is there any way of doing this?
Research and start using IBOutletCollection. It will give you an array of text fields that you can build in your storyboard XIB.
Note that you may need to consider the order of the array, and that you might want to sort it (possibly based on the tag of each view).
Technically, you could use string formats and KVC to do what you're currently trying to but it is far from ideal.
You can't just replace ce_1 ce_2 ce_3 with ce_[i] it doesn't work that way. You can only use [number] with an nsarray variable (or decendents).
for example:
NSArray* myArray = #[#1];
NSLog(#"%#", myArray[0]);
You might want to look into IBOutletCollection in order to achieve something similar to what you're looking for.
However, contrary to other answers here IBOutletCollection are ordered by how you link them in the interface builder.
Refer to this for IBOutletCollections: How can I use IBOutletCollection to connect multiple UIImageViews to the same outlet?
You can use IBOutletCollection. You can also use key-value coding:
for(NSUInteger i = 0; i < 13; i++)
{
[[self valueForKey:[NSString stringWithFormat:#"ce_%u", i]] setText: myArray[i]];
}
This will give you what you want.
The way I like to handle these situations is creating a temporary array containing all the text fields:
NSArray *allFields = #[_ce_1, _ce_2, _ce_3, ...];
NSInteger i = 0;
for (UITextField *tf in allFields)
{
tf.text = myArray[i];
i++
}
IBOutletCollection also work but sometimes it gets hard to figure out when you come back to your project which label is #3 or #5 and such... I find this works better for me usually :)

Showing a correct answer when someone selects a wrong one

Okay so I am new to objective-c and have used a couple different tutorials to build my first app that is a quiz. I had it completely done and I was somewhat satisfied.. then I realized that if someone selects the wrong answer it doesn't show them what the correct one is. So I have been trying to figure it out for a little bit. So I created a new label That stays hidden until an answer is chosen.
Is there an if statement I can use that can utilize the text I set buttons to? For example:
switch (QuestionSelected) {
case 0:
QuestionText.text = [NSString stringWithFormat:#"Question One"];
[Answer1 setTitle:#"Answer number 1" forState:UIControlStateNormal];
[Answer2 setTitle:#"Answer number 2" forState:UIControlStateNormal];
[Answer3 setTitle:#"Answer number 3" forState:UIControlStateNormal];
[Answer4 setTitle:#"Answer number 4" forState:UIControlStateNormal];
Answer1Correct = YES;
break;
This is what a question looks like within the category. My Answer1-4's are the buttons. And the Answer1Correct (depending on the answer) is a BOOL statement.
Is there a line that I can use in an if statement saying something along
if (Answer1Correct == YES) {
CorrectAnswer.text = [NSstring stringWithFormat:"#?", Answer1]; (The question mark would be for text the button was set to. I know that #i, can be used for integers but I don't know if I can use the text a button was set to)
I know that won't work but I am trying to wrap my head around how a lot of the valuables work. For instance, the problem I see is that within my questions I have BOOLs, and the only way it activates the BOOL is dependent on what they choose. For instance if they chose Answer2 in that previous statement it just sets runs my wrong answer one because this is what I have for the buttons
-(IBAction)Answer1:(id)sender{
if (Answer1Correct == YES) {
[self RightAnswer];
}
else{
[self WrongAnswer];
}
And then the same thing for the following three buttons.
I know I am probably all over the place but the reason I started this was to try and learn how to understand it. So how can I establish what is Correct when they choose the wrong one and how could I make the label show accordingly. I am not asking to be spoon fed, this is my first fully functional app that doesn't really have a full purpose other than myself trying to put things together and understand how things work together. So a little explanation or where I can read up on what is going on would be more appreciated then just an answer. I probably need to give a little more information so if I what I am asking doesn't make sense please ask. Thank you guys for the help.
Without seeing all of the code it's difficult, however one approach would be to have an array with the correct values in for each of the questions you would need to set a strong reference to this to ensure that it's not dealloc'd by arc.
in your .h
#property (nonatomic, strong)NSArray *correctAnswersArray;
#property (nonatomic, strong) IBOutlet UILabel *myLabelForIncorrectAnswer;
in your .m
-(void)viewDidLoad{
self.correctAnswersArray = [[NSArray alloc]initWithObjects:#"answer one", #"answer two", #"answer three", #"answer four",nil];
}
this can be reference using
self.correctAnswersArray[i];
where 'i' is the int value you require from the array - note that arrays are indexed starting at 0
With regards to your current conditional statement, you could set the label text using the array when you confirm if the answer is correct or incorrect so
if (Answer1Correct == YES) {
[self RightAnswer];
}
else{
[self WrongAnswer];
self.myLabelForIncorrectAnswer.text = self.correctAnswersArray[0];
}
and so on for each of the conditionals amending the array index number for each 'else' condition

Change UISegmentedControl selected index or value programmatically

I have a UISegmentedControl with two segments (index: 0 and 1) that I am trying to reset programmatically to 0. When I run the command below it does not reset the segment index to 0 as expected. Instead it highlights segment indexed 1.
[seg setSelectedSegmentIndex:0];
Oddly when I log the selected segment, it returns 0.
NSLog(#"seg setSelectedSegmentIndex %d", seg.selectedSegmentIndex);
Also oddly, after running setSelectedSegmentIndex:0 I cannot reselect segment 0 manually by touch, it seems to be locked to segment 1, until I tap 1 wherein it works as normal.
Here is the setup for the button:
itemArray = [NSMutableArray arrayWithObjects: #"Zero", #"One", nil];
seg = [[UISegmentedControl alloc] initWithItems:itemArray];
[seg setFrame:segRect];
seg.segmentedControlStyle = UISegmentedControlStyleBar;
seg.momentary = NO;
[seg addTarget:self action:#selector(someAction:) forControlEvents: UIControlEventValueChanged];
[mainView addSubview:seg];
NOTE: To restate what I was trying to accomplish with this question: I want to set a UISegmentedControl's selected index via a variable dynamically, get the appropriate visual feedback, and check the selected index in a method, to make something happen depending on the selected index. See my answer below for a solution.
Change in viewDidLoad:
[seg setSelectedSegmentIndex:index];
you change use the index value as your wish.
From code, you can just do seg.selectedSegmentIndex = someDefaultIndex.
Whether you should set it in viewDidLoad: or not depends entirely on the structure of your application. For example, if your app is starting up and loading the view for the first time and needs to set the control to whatever value it had during the previous run of the app, then it definitely makes sense to do it there.
Swift 3.0
segmentedControl.selectedSegmentIndex = #your value#
The correct answer is the #arcady bob one at this link.
I have posted an updated version for Swift 5, which I quote here:
yourSegmentedControl.selectedSegmentIndex = 0
yourSegmentedControl.sendActions(for: UIControl.Event.valueChanged)
If you use just the first line, the segmented will react accordingly graphically, but your IBAction associated with the segmented control won't be called. In a few words: the second line is like tapping on the segmented control. This is what I needed and what I was missing.
This helps sort the selected segment, in terms of the visual feedbak, that is after programatically setting the selected segment this will tint the segments properly.
for (int i=0; i<[seg.subviews count]; i++)
{
if ([[seg.subviews objectAtIndex:i] respondsToSelector:#selector(isSelected)] && [[seg.subviews objectAtIndex:i]isSelected])
{
[[seg.subviews objectAtIndex:i] setTintColor:[UIColor grayColor]];
}
if ([[seg.subviews objectAtIndex:i] respondsToSelector:#selector(isSelected)] && ![[seg.subviews objectAtIndex:i] isSelected])
{
[[seg.subviews objectAtIndex:i] setTintColor:[UIColor blackColor]];
}
}
To restate what I was trying to accomplish with this question: I want to set a UISegmentedControl's selected index via a variable dynamically, get the appropriate visual feedback, and check the selected index in a method, to make something happen depending on the selected index.
Originally I guessed that this is as simple as: mySegmentedController.selectedSegmentIndex = someIndex, then test for the index. But as stated in my question, it was not working for me. The behavior is odd. After running that code the appropriate button would not highlight.
Here is a project where I did fix it, so it works. Not sure exactly why it works. Possibly a delay I put in there, before setting the index. I will update and comment the project file if I can refine it.
http://owolf.net/uploads/StackOverflow/SegmentedControllers.zip
WARNING: This is not quite answering the question, but is closely related.
I had a similar issue. Most of the time my UISegmentedController worked fine. It has 4 segments, Off, 30, 60 and 120.
At random intervals (normally just prior to a release!!), the Off segment becomes un-selectable from the user interface. (I didn't try selecting it from code because that is not what I need to do and won't solve my problem.)
The solution is to delete that UISegmentedController from my XIB and replace it with a fresh (and exactly identical) one which then works fine.
I even CMD-C and CMD-V'd the offending UISegmentedController into another App's XIB. And the Off segment (i.e the one at index 0) was un-selectable in that XIB too.
This seems to be an issue with XCode.

Passing parameters to method, for accessing a CoreData attribute

I have customized the CoreDataBooks sample app and am adding a whole host of Bools. Essentially, I am turning it into a restaurant app, so I want to have Bool values for things like "Dinner" "Seafood" "Cocktails" "Kid Friendly" etc..
In my Detailview Controller, I have selected an item (book) and can alter the Bool values in CoreData - for an attribute named seafood, like this -
self.book.seafood = NO;
or
self.book.seafood = YES;
These values are each toggled with their own button (actually looks like a checkbox in my app)
However, because there may be 60 or 80 Bools, and I'd like to add more later, I'd like to write one function which I can use for all of them that uses a tag or passes a parameter. I'd like to write something like this, but I feel like I am missing a step. It clearly won't let me pass a parameter in to self.book.button:
-(IBAction) switchValue:(*UIButton)button{
tmpBool = self.book.button
if (tempBool == YES) {
self.book.button = NO;
UIImage *buttonImage = [UIImage imageNamed:#"checkOff.png"];
[self.button setImage:buttonImage forState:UIControlStateNormal];
NSLog(#"nwSwitched to NO");
}
else{
self.book.button = YES;
UIImage *buttonImage = [UIImage imageNamed:#"checkOn.png"];
[self.button setImage:buttonImage forState:UIControlStateNormal];
NSLog(#"nwSwitched to YES");
}
As it stands now, the only way I can make it work is with a huge series of if/thens or with switch, but I know there must be a more elegant way.
As Carl said, this was an inelegant approach. I instead switched and now keep all the tags in one CoreData attribute called "tags". To add and subtract them I simply add to the string or find a replace with #" ", to remove them. I can then search against them later.
In order to manage the long list of tags, and to add to them easily, I used a PickerView, which is set using an NSMutableArray, which I store in NSUserDefaults. (You have to make a mutable copy when you re-load it.)
Yes, you could keep the list of tags in CoreData, but this seemed easier for my purposes.

Reloading a non-table view (iOS)

I'm working on a painting app, and I want to user to be able to switch through paintings that are saved as PNGs to the documents directory. I've made an IBAction that does this:
-(IBAction)switchPage:(id)sender{
if ([sender tag] ==1)
{
currentImage=currentImage-1;
}
else
{
currentImage++;
}
[[NSUserDefaults standardUserDefaults] setInteger:currentImage forKey:#"currentDoc"];
[self.view setNeedsDisplay];
}
Just to let you know, I have 2 buttons, a back and forward, and they're connected to the same IBAction that's pasted above. The back button has a tag of 1, and the forward button doesn't have a tag. currentImage is an int that is used to set an image's name, so if currentImage =1, then it would set the image to image1. But that's not what I'm having trouble with, I'm having trouble "reloading" the view. I'm using setNeedsDisplay, but it's not working, and I know that reloadData is only for UITables, so what else could I use?
I've been searching for an answer, but none of the existing questions about this don't have a clear answer, or are under different circumstances.
Thanks for your time and/or dealing with a stupid question, as I'm new to Xcode.
-Karl
I see no view code in your post.
Assuming you have a UIImageView that is displaying the image you would just do:
myImageView.image = whateverUIImage;

Resources