Objective C - Random XIB pick - objective-c

I have already posted another question about this, but no one seemed to know how to do this.
I want my app to pick a random XIB file for me, but dont use the ones that have already been randomly picked.
So heres what i have set up as of right now, it seems to work, but i have to keep pressing the button over and over until it finds one that hasnt be used.
-(IBAction)continueAction:(id)sender{
random = arc4random() % 2;
if (random == 0 && usedQ2 == 0) {
Question_2 *Q2 = [[Question_2 alloc] initWithNibName:#"Question 2" bundle:nil];
Q2.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentModalViewController:Q2 animated:YES];
[Q2 release];
}
else if (random == 1 && usedQ3 == 0) {
Question_3 *Q3 = [[Question_3 alloc] initWithNibName:#"Question 3" bundle:nil];
Q3.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentModalViewController:Q3 animated:YES];
[Q3 release];
}
}
So as you can see i have it pick from a random number, and from their find the one that it matches.
Then you can see i have another part of my if statement that is checking to make sure it hasn't been used before.
each NIB file has its own usedQ(whatever Q it is), and when that Nib file is loaded it puts that usedQ as 1.
I think i could get by doing this, but in order to get rid of the constant button pushing, i will have to put loads of else statements with more else statements in them.
I have also tried running the
random = arc4random() % 2;
in a while statement and a for statement, i hoped that it would keep looking for a number until one that hasn't be used was found with no luck.
Any help? thanks!

Why don't you make a mutable array
and populate it with the names of all
your nibs.
Then read the count of the array and
generate a random number in that
range.
Extract the nib name at that index
and remove it from the array.
repeat steps 2-3.
//Setup your list at an appropriate place
NSMutableArray *nibs
= [[NSMutableArray alloc] initWithObjects: #"One Nib", #"Another
Nib", #"Last Nib", nil];
self.unusedNibs = nibs; //This should be a property you declare in
your header.
[nibs release];
-(IBAction)continueAction:(id)sender{
int random = arc4random() % [self.unusedNibs count];
NSString
*nibName = [self.unusedNibs objectAtIndex: random];
[self.unusedNibs removeObjectAtIndex:
random];
//Load nib here.
}

Related

(xcode objective-c) Array - finding a center of a object in an array

I'm pretty nooby when it comes to Xcode but here goes.
I have an NSArray with 5 UIImageView's, I have a script which will pick a random UIImageView, but I need it to then output the center of the image that was chosen.
My code looks like this:
NSMutableArray *Invader = [[NSMutableArray alloc] init];
[Invader addObject:H1];
[Invader addObject:H2];
[Invader addObject:H3];
[Invader addObject:H4];
[Invader addObject:H5];
Then like this to choose where to move a new UIImageView to:
if (FIRE1 == YES){
NSObject *Inv1 = [Invader randomObject]; //randomObject picks a random Invader
InvaderBullet1.hidden = NO;
InvaderBullet1.center = CGPointMake(Inv1.center.x, Inv1.center.y)
}
The error it's giving me is Property 'center' not found on object of type 'NSObject *'
I'm hoping this makes sense to anyone and that anyone can help me.
Thanks
NSObject obviously doesn't have a center property. Though you're not adding NSObjects to your array, you're adding (I assume) UIImageViews. So if you're adding UIImages, you can remove UIImages as well.
if (FIRE1 == YES){
NSObject *Inv1 = [Invader randomObject];
if ([Inv1 isKindOfClass:[UIImageView Class]]) //randomObject picks a random Invader
((UIImageView*)InvaderBullet1).hidden = NO;
((UIImageView*)InvaderBullet1).center = CGPointMake(Inv1.center.x, Inv1.center.y)
}
You simply need to specify the correct type for your variable:
if (FIRE1 == YES){
UIImageView *Inv1 = [Invader randomObject]; //randomObject picks a random Invader
InvaderBullet1.hidden = NO;
InvaderBullet1.center = CGPointMake(Inv1.center.x, Inv1.center.y)
}
This is assuming that your randomObject method (looks like a category method on NSArray) has a return type of id. If its return type is NSObject * then you need to do a cast:
UIImageView *Inv1 = (UIImageView *)[Invader randomObject]; //randomObject picks a random Invader
(Minor note: I recommend to stick to the established naming conventions. In this case: variables usually start with a lower-case character. You're doing other people that need/want to work with your code a favor.)

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);
}

UICollectionView Very Slow Custom Layout

I am using a UICollectionView with a custom layout that lays out cells in a grid format. There can be well over 50 rows and 50 columns. Scrolling occurs both vertically and horizontally. Currently, I am doing all of the layout setup in prepareLayout and storing it in arrays:
- (void)prepareLayout {
NSMutableArray *newLayoutInfo = [[NSMutableArray alloc] init];
NSMutableArray *newLinearLayoutInfor = [[NSMutableArray alloc] init];
NSInteger sectionCount = [self.collectionView numberOfSections];
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:0 inSection:0];
self.heightForRows = [delegate collectionViewHeightForAllRows];
self.totalWidthsForRows = [[NSMutableArray alloc] init];
for (int i = 0; i < sectionCount; i++) {
[self.totalWidthsForRows addObject:[NSNumber numberWithInt:0]];
}
for (NSInteger section = 0; section < sectionCount; section++) {
NSMutableArray *cellLayoutInfo = [[NSMutableArray alloc] init];
NSInteger itemCount = [self.collectionView numberOfItemsInSection:section];
for (NSInteger item = 0; item < itemCount; item++) {
indexPath = [NSIndexPath indexPathForItem:item inSection:section];
UICollectionViewLayoutAttributes *itemAttributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
itemAttributes.frame = [self frameForCellAtIndexPath:indexPath];
[cellLayoutInfo addObject:itemAttributes];
[newLinearLayoutInfor addObject:itemAttributes];
}
[newLayoutInfo addObject:cellLayoutInfo];
}
self.layoutInfo = newLayoutInfo;
self.linearLayoutInfo = newLinearLayoutInfor;
}
Then in layoutAttributesForElementsInRect I have:
- (NSArray*)layoutAttributesForElementsInRect:(CGRect)rect {
NSArray *rows = [self.linearLayoutInfo filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(UICollectionViewLayoutAttributes *evaluatedObject, NSDictionary *bindings) {
return CGRectIntersectsRect(rect, [evaluatedObject frame]);
}]];
This works okay, but it is laggy and jumpy when I have over 50 columns and 50 rows. The problem I now have is that I must set
-(BOOL)shouldInvalidateLayoutForBoundsChange {
return YES;
}
This makes it prepare the entire layout every time the bounds change, which, needless to say, has a huge impact on performance and you can barely scroll. The cells consist of just text with an opaque background, so there is no issue there.
I am sure I am not doing this right and that there must be a better way. Thanks for the help in advance.
In custom flow layout I do this and it seems to help:
- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds {
return !(CGSizeEqualToSize(newBounds.size, self.collectionView.frame.size));
}
-(BOOL)shouldInvalidateLayoutForBoundsChange {
return YES;
}
Causes the layout to do prepareLayout() every time it scrolls, which means anything of heavy computing in prepare will lead to a laggy practice, so one possible direction to solve this is to check what's really taking much time. One possibility is what's inside
for (NSInteger section = 0; section < sectionCount; section++)
{
// generate attributes ...
}
in order to generate attributes for the layout. Every time it scrolls, every time this generalization reruns, so that it impacts on the scroll appear to be jumpy and clumsy. So in order to solve this issue, or at least sort out that this is not the really trouble, I suggest setting a flag in this layout algorithm, say, isScrolling, standing for the situation where the layout needs to prepare. Every time in prepareLayout() check the flag, if it is YES, then we'll know there's no need to do for loop to regenerate all the attributes, which alreay exsit ever since the first time the layout is initialised.
ok--I understand now. Here's what I recommend: create 3 collection views... one for the column headers (where each cell is column header), one for the row leaders (each cell = 1 row leader) and one collection view for your cells. Then when the scroll position of any collection view is changed by the user, update the scroll positions for the other 2 collection views as appropriate.

UISegmentedControl writing text labels problem

Goodday,
I got the following problem with this code:
-(void)textpopup:(UISegmentedControl *)sender {
int nummer = sender.tag;
if (sender.tag) {
if(sender.selectedSegmentIndex == 0 || sender.selectedSegmentIndex == 1){
beoordeling = [[UITextField alloc] init];
beoordeling.frame = CGRectMake(50 , nummer * 117 + 275 , scrollView.frame.size.width - 100 , 35);
beoordeling.autoresizingMask = (UIViewAutoresizingFlexibleWidth);
beoordeling.borderStyle = UITextBorderStyleLine;
beoordeling.tag = nummer;
[scrollView addSubview:beoordeling];
}
if(sender.selectedSegmentIndex == 2 || sender.selectedSegmentIndex == 3){
if (beoordeling.tag == sender.tag) {
[beoordeling removeFromSuperview];
}
}
}
}
i shall explain the scenario. I got some dynamic UISegmentedControls. At the moment there are 12 of them. At the first 2 segments chosen, a textfield needs to popup. This goes well. But after choosing the first 2 segments for a while and when i go to segments 2 and 3, sometimes the textfields won't remove.
I expected that the textfields which are written when i push segment 0 and 1 are removed when segment 2 and 3 are pushed.
Am i missing something?
EDIT:
At first i want to say, that i never know in advance how many UITextFields i got. When segments 0 and 1 are chosen, a UITextField needs to popup to that corresponding UISegmentedControl. And when segments 2 and 3 are chosen, the UITextField needs to stay away. But i got that fixed now in the following way.
-(void)textpopup:(UISegmentedControl *)sender {
int nummer = sender.tag;
if(sender.selectedSegmentIndex == 0 || sender.selectedSegmentIndex == 1){
// Before i add a new UITextField, the old one has to be removed.
UITextField *text = (UITextField *)[beoordeling viewWithTag:sender.tag];
[text removeFromSuperView];
beoordeling = [[UITextField alloc] init];
beoordeling.frame = CGRectMake(50 , nummer * 117 + 275 , scrollView.frame.size.width - 100 , 35);
beoordeling.autoresizingMask = (UIViewAutoresizingFlexibleWidth);
beoordeling.borderStyle = UITextBorderStyleLine;
beoordeling.tag = nummer;
[scrollView addSubview:beoordeling];
}
else if(sender.selectedSegmentIndex == 2 || sender.selectedSegmentIndex == 3) {
UITextField *tf = (UITextField *)[beoordeling viewWithTag:sender.tag];
tf.text = nil;
[tf removeFromSuperview];
}
}
The second if should be an else if, because it should execute only if the first one doesn't (the selected index can't be both 0 and 2).
What you're trying to do depends on how you've defined beoordeling. I suggest having it as an instance variable, possibly even an IBOutlet. Release in your class's dealloc. Then, in the first if clause, write
if (!beoordeling) {
beoordeling = [[UITextField alloc] init];
// Other setup
}
[scrollView addSubview:beoordeling];
I don't quite understand why you're checking for the tag, but to remove in the second if, just call removeFromSubview.
Do all your segmented controls invoke the same behavior? If you have only one beorrdeling that you're setting the tag on, then you don't need to bother checking for the tag. Just add/remove it, using removeFromSubview. If you have as many of text fields as segmented controls, maybe KVC would be what you want. If your segmented controls' tags go from 0–11, you might have beoordeling0, beoordeling1, beoordeling2, and so on. Then, to get the one you want, use something like this:
beoordeling = (UITextField *)[self valueForKey:[NSString stringWithFormat:#"beoordeling%d", sender.tag]];
Not really sure what you are trying to achieve here, but you do need to release your UITextField object beoordeling or you will leak. AddSubview adds one to the retain count, so should be safe to release straight away:
[scrollView addSubview:beoordeling];
[beoordeling release];

How do I init a NSArray with a set of images that reocur?

EDIT: I think i asked something really wrong over here, and i am sorry. What i am doing, is a mock of a slot machine, but i don't think that it will be wise(just don't want to insult myself) of me to put 250(50*5) images in the UIPICKERVIEW, There is no reason doing that, My idea now is to use only the 6 images for each column in deferent order and then when i spin just go again and again over the 30(6*5) images and spin them, that will give the same effect while using less resources, right? I can't see what i was thinking, and thank you all for the answers
Implementing a complex UIPickerView (5 components) in iOS, I am stuck on this problem...
I have 5 NSArrays (for each component in the picker...not mutable) and I have 6 images.
I need to init every array with the set of images, that will populate the array for 50 images, set after set (I'm guessing some kind of for loop that will run 8 times and populate the array with 2 more images at the end?)
In every array the images need to be in a different order, but again, for the same array the set has to be the same all the way.
When I say a set I mean that I need to put the images in the same order again and again, not that they are in another array or anything like that....
Can any one help with this?
10x,
Erez
Not sure I understood the question, but, if I did, you may do:
NSArray *set = [NSArray arrayWithObjects:[UIImage imageNamed:#"img1.png"],/* all the images */ , nil];
NSMutableArray *tmp = [[NSMutableArray alloc] init];
for (int i = 0; i<50; i++) {
[tmp addObject:[set objectAtIndex:(i%6)]];
}
then you may use tmp as common NSArray, or do something like
NSArray *result = [NSArray arrayWithArray:tmp];
ps. don't forget to release tmp when you finished.
UIImage *image;
int randomIndex;
NSCountedSet *set = [[NSCountedSet alloc] initWithCapacity:50];
NSMutableArray *arrays = [[NSMutableArray alloc] initWithCapacity:5];
for ( int i = 0; i < 5; i++ ) {
while ( [set count] < 50 ) {
randomIndex = arc4random() % 6;
image = [images objectAtIndex:randomIndex];
switch ( [set count] ) {
case 48:
case 49:
if ( [set countForObject:image] < 9 ) {
[set addObject:image];
}
break;
default:
if ( [set countForObject:image] < 8 ) {
[set addObject:image];
}
}
}
[arrays addObject:[set allObjects]];
[set removeAllObjects];
}
[set release];
The idea would be to use a counted set and get the count of elements added to it. This way we can keep track of objects being added.