Random order of buttons - iphone

I have created a quiz which draws questions from a plist into an array and displays the question in a uilabel and the three possible answers in uibuttons. My question is how can I create a random order for the buttons so that the three answers don't always show up in the same order?
Any help would be most appreciated.
Jamie

Going along with #Abizern's answer. It would be more efficient to have the fixed buttons and randomise the array. You could do something like this:
//NSMutableArray *questions = [[NSMutableArray alloc] initWithObjects:#"1", #"2", #"3", #"4", nil];
NSMutableArray *unorderedQuestions = [[NSMutableArray alloc] init];
for (int i=0; i < [questions count]; i++) {
int lowerBound = 0;
int upperBound = 100;
int rndValue = lowerBound + arc4random() % (upperBound - lowerBound);
[unorderedQuestions addObject:[[NSArray alloc] initWithObjects:[questions objectAtIndex:i], [NSString stringWithFormat:#"%i", rndValue], nil]];
}
NSArray* sortedArray = [unorderedQuestions sortedArrayUsingFunction:order context:NULL];
//or
// questions = [unorderedQuestions sortedArrayUsingFunction:order context:NULL];
NSLog(#"%#", sortedArray);
and put this function somewhere:
static NSInteger order (id a, id b, void* context) {
NSString* catA = [a lastObject];
NSString* catB = [b lastObject];
return [catA caseInsensitiveCompare:catB];
}
This basically assigns a random number (between 0 and 100) to each question and sorts it by that number. You can then just make sortedArray global (or possibly just overwrite your questions array) and display objects sequentially and they will be shuffled.
[btnA setTitle:[[[questions objectAtIndex:r] objectAtIndex:0] objectForKey:#"A"] forState:UIControlStateNormal];
Judging by your code, you may need to amend a few lines: (but you could also create a currentQuestionsArray and assign it to [questions objectAtIndex:r] before the code above)
for (int i=0; i < [questions count]; i++) {
//to
for (int i=0; i < [[questions objectAtIndex:r] count]; i++) {`
and
[unorderedQuestions addObject:[[NSArray alloc] initWithObjects:[questions objectAtIndex:i], [NSString stringWithFormat:#"%i", rndValue], nil]];
//to
[unorderedQuestions addObject:[[NSArray alloc] initWithObjects:[[questions objectAtIndex:r] objectAtIndex:i], [NSString stringWithFormat:#"%i", rndValue], nil]];`
I think this is the most robust solution, as in the future you may have a different number of answers for a particular question and would not need to edit this to do so!

Rather than go through recreating the buttons and positioning them, just have three buttons in fixed locations and assign the answers to them randomly.
How do you assign them randomly? you could either take elements out of the array in a random order and assign them, or, you could just shuffle the array yet always have the object at index 0 appear on the first button and the object at index 2 apear on the second button etc, which I think is easier.
You can see an implementation of array shuffling in this question What's the Best Way to Shuffle an NSMutableArray?

You can place the buttons anywhere e.g. by setting their center property with firstButton.center = newCenter;.
I assume you have created your buttons in Interface Builder. Therefore, you have already the 3 centers that only have to be permuted randomly. With 3 buttons that I call 1, 2 and 3, you have the following 6 possibilities: (123), (132), (213), (231), (312) and (321). Thus you have to select 1 of 6 possibilities randomly.
The function arc4random() generates a random integer. To get one between 0 and x, one has to call arc4random() % (x+1). You can use the result to select the right permutation e.g. in the following code example:
Store first the 3 centers:
CGPoint center1 = myButton1.center;
CGPoint center2 = myButton2.center;
CGPoint center3 = myButton3.center;
and generate new locations later:
int permutation = arc4random() % 6;
switch (permutation) {
case 0:
myButton1.center = center1;
myButton2.center = center2;
myButton3.center = center3;
break;
case 1:
myButton1.center = center1;
myButton2.center = center3;
myButton3.center = center2;
break;
case 2:
myButton1.center = center2;
myButton2.center = center1;
myButton3.center = center3;
break;
case 3:
myButton1.center = center2;
myButton2.center = center3;
myButton3.center = center1;
break;
case 4:
myButton1.center = center3;
myButton2.center = center1;
myButton3.center = center2;
break;
case 5:
myButton1.center = center3;
myButton2.center = center2;
myButton3.center = center1;
break;
default:
break;
}

First you need to calculate your all UIButtons means how many buttons you need to use there and then use for loop to create uibutton in radom order and use tag to perform action on each button.`
-(void)button:(id)sender {
int btnn = 0;
int spacex = 10;
int spacey=20;
int k=0;
saveBtn = [[NSMutableArray alloc] init];
for (int i=0; i<96; i++) {
if (btnn>14) {
spacey=spacey+32;
spacex = 10;
btnn = 0;
}
else {
btnn++ ;
k++;
UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom];
btn.frame = CGRectMake(spacex, spacey, 30.0, 32.0);
[btn setShowsTouchWhenHighlighted:YES];
int idx;
idx = arc4random()%[arr count];
NSString* titre1 = [arr objectAtIndex:idx];
[btn setTitle: titre1 forState: UIControlStateNormal];
[btn setBackgroundImage:[UIImage imageNamed:#"Orange.png"] forState:UIControlStateNormal];
[btn setTitleColor: [UIColor blackColor] forState: UIControlStateNormal];
[btn.titleLabel setFont:[UIFont fontWithName:#"TimesNewRomanPS-BoldMT" size:21.0]];
spacex = spacex + 30;
btn.tag=k;
[btn addTarget:self action:#selector(aMethod:)forControlEvents:UIControlEventTouchUpInside];
[saveBtn addObject:btn];
[self.view addSubview:btn];
}
}
use the above method i hope it will work for you.if you have any query then let me know.Thanks

Related

Adding UILabels Dynamically in a Loop

I am looking for a function or a loop prototype to add UILabels to a view on iPhone. The reason for this is I won't know in advance how many labels I need to add, so they will need to be added dynamically.
My pseudo code is as follows. The idea is that each label is given a string and then placed in the next screen, hence the +self.view.frame.size.width statement. Paging etc work perfectly, the problem is all the labels appear to be ending up on the second screen i.e. Label 3 appears on top of label 2. The issue would appear to be I am always referencing altLabel, and as such once I move to the second position, I am constantly referencing that position and never moving once there.
I can use the 'count' variable to multiple the screen width, but if I do that, every time I update the label text, it will overwrite the previous.
int count = 0;
int maxNumber = 10;
while(count < maxNumber) {
//Add a label
UILabel *altlabel; //Declare the label
if (count > 0) {
//Move the label
altlabel = [[UILabel alloc] initWithFrame:CGRectMake(CGRectGetMinX(altlabel.frame)+self.view.frame.size.width,10,300,25)];
altlabel.text = [NSString stringWithFormat:#"%# %# (%d)", _name,_age, class+count];
}
else {
altlabel = [[UILabel alloc] initWithFrame:CGRectMake(10,10,300,25)];
altlabel.text = [NSString stringWithFormat:#"%# %# (%d)", _name,_age, class];
}
altlabel.textColor = [UIColor greenColor];
[altlabel sizeToFit];
[_scrollView addSubview:altlabel];
count++;
}
The problem is this line:
UILabel *altlabel; // Declare the label
if (count > 0) {
//Move the label
altlabel = [[UILabel alloc] initWithFrame:CGRectMake(CGRectGetMinX(altlabel.frame)+self.view.frame.size.width,10,300,25)];
altlabel.text = [NSString stringWithFormat:#"%# %# (%d)", _name,_age, class+count];
}
You are setting up the frame using altlabel.frame, but there altlabel is not setted up: you redeclared it on the first line with UILabel *altlabel.
With this code every label but the first will have the same frame. Try with this:
int count = 0;
int maxNumber = 10;
CGRect rect;
while(count < maxNumber) {
// Add a label
UILabel *altlabel; // Declare the label
if (count > 0) {
//Move the label
altlabel = [[UILabel alloc] initWithFrame:CGRectMake(CGRectGetMinX(rect.frame)+self.view.frame.size.width*count, 10, 300, 25)];
altlabel.text = [NSString stringWithFormat:#"%# %# (%d)", _name,_age, class+count];
} else {
altlabel = [[UILabel alloc] initWithFrame:CGRectMake(10, 10, 300, 25)];
altlabel.text = [NSString stringWithFormat:#"%# %# (%d)", _name,_age, class];
}
rect = altlabel.frame;
altlabel.textColor = [UIColor greenColor];
[altlabel sizeToFit];
[_scrollView addSubview:altlabel];
count++;
}
Now the frame of new label is saved in a temporary var (CGRect frame) and you can use it.
This code should work:
int count = 0;
int maxNumber = 10;
while(count < maxNumber) {
//Add a label
UILabel *altlabel; //Declare the label
if (count > 0) {
//Move the label
altlabel = [[UILabel alloc] initWithFrame:CGRectMake(CGRectGetMinX(altlabel.frame)+(self.view.frame.size.width*count),10,300,25)];
altlabel.text = [NSString stringWithFormat:#"%# %# (%d)", _name,_age, class+count];
}
else {
altlabel = [[UILabel alloc] initWithFrame:CGRectMake(10,10,300,25)];
altlabel.text = [NSString stringWithFormat:#"%# %# (%d)", _name,_age, class];
}
altlabel.textColor = [UIColor greenColor];
[altlabel sizeToFit];
[_scrollView addSubview:altlabel];
count++;
}
A much cleaner approach would be to predetermine the frame of your label before creation and then add it as a subview (or perhaps save each label to an array for reference later on).
NSInteger numberOfLabels = 10;
CGFloat labelHeight = 25.0;
CGFloat padding = 10.0;
for(NSInteger index = 0; index < numberOfLabels; ++index)
{
CGRect frame = CGRectMake(10.0, (index * (labelHeight + padding)), 300.0, labelHeight);
UILabel *label = [[UILabel alloc] initWithFrame:frame];
[[self view] addSubview:label];
}
You may want to assign the label a tag with setTag: to reference each label, or gather then from an array rather than looping through subviews to gather or append text after they have been created.
I think the problem will be that your label 'altLabel' is being reinitialised each time the loop runs through and therefore the frame of the label is also being reset.
This means that every time a new label is created, the frame will be in exactly the same place.
A simple way to fix this would simply be to move the initial declaration of the label to outside the while loop.
A potentially better and more efficient fix would require a rework of the loop used. When I do things like this, I tend to use a for loop as you have use of an index to help position things:
for(int i = 0; i < 10; i++)
{
//Getting the x-position correct here may take a little trial and error
UILabel *altLabel = [[UILabel alloc] initWithFrame:CGRectMake(INSET + i*LABEL_WIDTH, 10, 300, 25)];
[altLabel setText:[NSString stringWithFormat:#"%# %# (%d)", _name,_age, class+count]];
[altLabel setTextColor:[UIColor greenColor]];
[altLabel sizeToFit];
[_scrollView addSubview:altLabel];
}
This should then mean you don't need a counter.
Hopefully this helps!
Matt

How to get UILabel Tags in iPhone

I am creating labels dynamically from NSMutableArray.While creating labels I have set tags for each label. I have one NSMutableArray named wordArray. Now, I want to check my string is available in wordArray or not,
I can check this using :
[wordArray containsObject:wordStr];
For creating labels dynamically :
UILabel *wordLabl;
int tagValue3 = 1;
for (int iloop = 0; iloop < [wordArray count]; iloop++)
{
wordLabl = [self addwordLabelRect:CGRectMake(80 * iloop + 20, 420 , 100, 20)andTag:tagValue3];//30 + 35 30 * iloop+
[self.view addSubview:wordLabl];
tagValue3 += 1;
}
-(UILabel *)addwordLabelRect:(CGRect)rect andTag:(int)integerValue
{
wordLabel = [[UILabel alloc] init];
wordLabel.frame = rect;
wordLabel.userInteractionEnabled = YES;
wordLabel.tag = integerValue;
wordLabel.backgroundColor = [UIColor clearColor];
wordLabel.font = [UIFont systemFontOfSize:15];
wordLabel.text = [NSString stringWithFormat:#"%#",[wordArray objectAtIndex:integerValue - 1]];
wordLabel.textAlignment = NSTextAlignmentCenter;
wordLabel.textColor = [UIColor whiteColor];
return wordLabel;
}
Using above code I am creating labels and Tags.
But,If wordArray contains the string I want to change the textColor of that label.I think this can be done using Tag , but how can I get the tag value of the label.
Sorry, I overlooked you code... You just need to add following lines where you want to access your appropriate label:
if([wordArray containsObject:wordStr])
{
UILabel *label = (UILabel *) [self.view viewWithTag:([wordArray indexOfObject:wordStr] - 1)];//since u started tag assignment from 1
label.textcolor = [UIColor yellowColor];
}
I guess you're doing something like that to set the tags ?
for (NSUInteger i = 0; i < [wordArray count]; ++i) {
UILabel * label;
// setup your label...
[label setTag:i];
[yourView addSubview:label];
}
If so, just do :
NSUInteger index = [wordArray indexOfObject:wordStr];
if (index != NSNotFound) {
UILabel * label = [yourView viewWithTag:index];
// do whatever you want with your label
}
Good luck.
if you want to get UILabel from its Tag.
you can use following loop
int i=0;
for (NSObject *view in self.View.subviews)
{
if ([view isKindOfClass:[UILabel class]])
{
label = (UILabel *)[[self view] viewWithTag:wordArray[i]];
NSLog(#"%#",label.text);
//here you get your label
}
i++;
}

Can't hide created UIButton

I have three UIButtons which display in a random order using:
NSMutableArray *indexArray = [NSMutableArray arrayWithObjects:
[NSValue valueWithCGRect:CGRectMake(20, 187, 280, 44)],
[NSValue valueWithCGRect:CGRectMake(20, 258, 280, 44)],
[NSValue valueWithCGRect:CGRectMake(20, 330, 280, 44)], nil];
//Randomize the array
NSUInteger count = [indexArray count];
for (NSUInteger i = 0; i < count; ++i) {
int nElements = count - i;
int n = (arc4random() % nElements) + i;
[indexArray exchangeObjectAtIndex:i withObjectAtIndex:n];
}
//Assign the frames
button1.frame = [((NSValue *)[indexArray objectAtIndex:0]) CGRectValue];
button2.frame = [((NSValue *)[indexArray objectAtIndex:1]) CGRectValue];
button3.frame = [((NSValue *)[indexArray objectAtIndex:2]) CGRectValue];
For some reason I an unable to hide these buttons after they display a number of items. I have tried for example
button1.hidden = YES; and also
[self.button1.hidden = YES];
Any ideas? Any help would be most appreciated.
Jamie
Pass tag to Buttons and use below code
for (UIButton *btn in [self.view subviews])
{
if (btn.tag==1)
{
[btn removeFromSuperview];
}
}
and your problem will be resolved and revert me..
These buttons are IBOUTLET?
You can think of another way to hide
like, '[UIView viewWithTag:(NSInteger)]
sample code is here
UIButton *tmpBtn = (UIButton *)[self.view viewWithTag:1]; // tag is your choice
tmpBtn.hidden = YES
To do this I use this:
if ([questions count]== 11)
{ button1.hidden = YES; button2.hidden = YES; button3.hidden = YES }
I would suggest you check two things:
that you effectively take the branch;
that your button* variables are not nil.
e.g.:
if ([questions count]== 11)
{
NSLog(#"Enter branch with %x, %x, %x", button1, button2, button3);
button1.hidden = YES; button2.hidden = YES; button3.hidden = YES;
}
(ignore the warning that you will get on NSLog).

How to display (5 or 7 or 8 etc) images from 25 images randomly without repetition from array in iphone

In my FirstViewcontroller I have picker containing 1 to 25 values, if the user select 5 in the picker on the next screen it should display 5 images randomly out of 25 images without repetition of that 5 images. If user selects another number means it takes that number as input and it should display images according to that number. If I run my code means every time it showing all 25 images not selected number images. Here is my code
- (void)viewDidLoad {
myImageNames = [[NSMutableArray alloc] init];
[myImageNames addObject:[UIImage imageNamed:#"Red.png"]];
[myImageNames addObject:[UIImage imageNamed:#"Blue.png"]];
[myImageNames addObject:[UIImage imageNamed:#"LightRed.png"]];
[myImageNames addObject:[UIImage imageNamed:#"Orange.png"]];
.
.
.
timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(ChangingImgView:) userInfo:nil repeats:YES];
}
- (void) ChangingImgView:(int)selectedNumberFromPicker {
selectedNumberFromPicker = [num intValue];
for (int i=0; i < selectedNumberFromPicker; i++) {
index = arc4random() % 25;
NSString *strIndex = [NSString stringWithFormat:#"%i",index];
[myImageNames addObject:strIndex];
imgBtn = [UIButton buttonWithType:UIButtonTypeCustom];
imgBtn.frame = CGRectMake(250, 300, 170, 220);
UIImage *img = [myImageNames objectAtIndex:index];
[imgBtn setBackgroundImage:img forState:UIControlStateNormal];
[imgBtn addTarget:self action:#selector(ClickingOnImage) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:imgBtn];
}
}
Please help me.
you can also use NSSet instead of using NSMutableArray
doc apple
You can use sets as an alternative to arrays when the order of elements isn’t important and performance in testing whether an object is contained in the set is a consideration—while arrays are ordered, testing for membership is slower than with sets.
Firstly suffle your myImageNames which has images using this link.
Now display first 5 images from myImageNames.
int count = 5;
NSMutableArray *inputImages = [myImageNames mutableCopy];
NSMutableArray *randomImages;
for (int i = 0; i<count; i++) {
int index = arc4random_uniform([inputImages count]);
[randomImages addObject:[inputImages objectAtIndex:index]];
[inputImages removeObjectAtIndex:index];
}
Try using this for loop to get the particular number of images for random indexes. Use selectedImgArray to display the selected images. myImageNames is your original array.
NSInteger index;
NSMutableArray *selectedImgArray = [[NSMutableArray alloc] init];
for (int i=0; i < selectedNumberFromPicker; i++) {
index = arc4random() % 25;
UIImage *selImg = (UIImage *)[myImageNames objectAtIndex:index];
if ([selectedImgArray containsObject:selImg]) {
i--;
continue;
}
[selectedImgArray addObject:selImg];
}

UILabel multi-set values

I know this is going to sound like a trivial question but I have defined 50 labels in my *.h file
UILabel *tempLabel1;
UILabel *tempLabel2;
UILabel *tempLabel3;
...
UILabel *tempLabel50;
In my *.c file I want to set the value for each of those labels but I don't want to do it manually or write them out over and over again.
//Instead of doing this
tempLabel1.text = #"1";
tempLabel2.text = #"2";
...
tempLabel50.text = #"50";
//I would like to do something like this
//I know this isn't correct syntax but want to know if something like
//this can be done?
for (int i = 0; i < 50; i++)
{
tempLabel[i].text = #"%d", i+1;
}
Well one way that comes to mind (not the cleanest but A way) is to do the following:
UILabel *tempLabels[50];
The problem you run into then is you can't use IB to connect them. Instead what you do is use the tag property in each of them (this means you need to set the tag for all 50 UILabels). To properly connect them you run this in viewDidLoad:
for (index = 1; index < 50; ++index)
{
tempLabels[index] = (UILabel *) [self.view viewWithTag: index];
}
Bingo! Now anywhere in your code if you need to change the labels you can do the following:
for (index = 1; index < 50; ++index)
{
tempLabels[index].text = [NSString stringWithFormat: #"Number %d", index];
}
It's kind of tedious setting the tags, but once you are done, you are done.
BTW, unlike the other solutions, you can use IB to create your labels.
this would be a good start for you, I guess.
NSMutableArray *_labels = [NSMutableArray array];
for (int i = 1; i < 50; i++) {
UILabel *_newLabel = [[UILabel alloc] init]; // please, make the init method as you wish
// ... any customization of the UILabel
[_newLabel setText:[NSString stringWithFormat:#"%d", i]];
[_newLabel setTag:i];
[_labels addObject:_labels];
[self.view addSubview:_newLabel];
}
// ... and then you can do the following
for (int i = 0; i < _labels.count; i++) {
[((UILabel *)[_labels objectAtIndex:i]) setText:[NSString stringWithFormat:#"%d", i]];
}
You can add the UILabels programmatically and hence have access to them - so you can set their text value as well.
You can add a UILabel like this:
UILabel *theLabel = [[UILabel alloc] initWithFrame:CGRectMake(50, 100, 200, 100)]; //need to calculate the x,y coordinates in dependency of i
theLabel.text = [NSString stringWithFormat:#"%i",i];
[self.view addSubview:theLabel]; // add it to your view
myLabel.backgroundColor = [UIColor clearColor]; // change the color
If you need access to the labels later as well, just add them to an NSArray that you keep.