I am new to Objective-C and learning by trial and error! Please forgive me if this question is somewhat naïve.
I've created an array of images and need to shuffle them. I've used the advice given here:
What's the Best Way to Shuffle an NSMutableArray?
And now I need to implement the method 'shuffle'. I have separate category for my array and its implementation:
#interface theKid : NSObject {
NSMutableArray* _cards;
}
#property(strong, nonatomic, readonly) NSMutableArray *cards;
- (UIImage*) shuffledCard;
#end
But now I can't figure out how to implement 'shuffle':
#implementation theKid
- (NSMutableArray *) cards {
_cards = [[NSMutableArray alloc] initWithObjects:
[UIImage imageNamed:#"One"],
[UIImage imageNamed:#"Two"],
[UIImage imageNamed:#"Three"],
[UIImage imageNamed:#"Four"], nil];
return _cards;
}
- (UIImage*) shuffledCard {
shuffle *cards;
return [cards objectAtIndex:0];
}
#end
This just effects a blank screen when I try to call 'shuffledCard'. Any advice much appreciated.
Hi i have pasted few lines of code below which will Shuffle an NSMutable Array exactly like what you want.
NSMutableArray * _cards = [[NSMutableArray alloc] init];
[_cards addObject:[UIImage imageNamed:#"One.png"]];
[_cards addObject:[UIImage imageNamed:#"Two.png"]];
[_cards addObject:[UIImage imageNamed:#"Three.png"]];
[_cards addObject:[UIImage imageNamed:#"Four.png"]];
[_cards addObject:[UIImage imageNamed:#"Five.png"]];
// Add the below code in the Shuffle Method
NSUInteger count = [_cards count];
for (NSUInteger i = 0; i < count; ++i)
{
NSInteger remainingCount = count - i;
int exchangeIndex = i + arc4random_uniform(remainingCount);
[_cards exchangeObjectAtIndex:i withObjectAtIndex:exchangeIndex];
UIImage * image = [_cards objectAtIndex:i];
}
Just use this api for shuffling of object exchangeObjectAtIndex:withObjectAtIndex: Refer this how to shuffle object in NSMutableArray?
Related
I am teaching myself Objective C, and I am trying to progress with the idea of model-view-controller. My question is: what is the correct way of creating data in a model class? I have an example to illustrate what I mean, it's a bit long but a really simple question.
The following code is from a memory game I just finished. It works fine, I have separated the data from the view controller and so I have a separate class to hold the images for the cards. Here is that class. By the way, it all works, the only thing important for my question is that there is an array declared so feel free to skip most of the code that does the duplicating of the images and randomising. Anyway, here it is:
import "Cards.h"
#implementation Cards
- (NSMutableArray*) createCardImages{
NSMutableArray *Fruits = [[NSMutableArray alloc] initWithObjects:
[UIImage imageNamed:#"Apple.png"],
[UIImage imageNamed:#"Banana.png"],
[UIImage imageNamed:#"Pear.png"],
[UIImage imageNamed:#"Strawberry.png"],
[UIImage imageNamed:#"Lemon.png"],
[UIImage imageNamed:#"Orange.png"],
[UIImage imageNamed:#"Pineapple.png"],
[UIImage imageNamed:#"Kiwi.png"],
[UIImage imageNamed:#"Prune.png"],
[UIImage imageNamed:#"Peach.png"],
nil];
NSUInteger tileCount = [Fruits count];
for (NSUInteger i = 0; i < tileCount; ++i) {
NSInteger nElements = tileCount - i;
NSInteger n = (arc4random() % nElements) + i;
[FullList exchangeObjectAtIndex:i withObjectAtIndex:n];
}
NSArray *Shortlistprep1 = [Fruits subarrayWithRange:NSMakeRange(0, 10)];
NSArray *ShortListprep2=[Shortlistprep1 arrayByAddingObjectsFromArray:Shortlistprep1];
NSMutableArray *ShortList = [[NSMutableArray alloc]init];
ShortList = [NSMutableArray arrayWithArray:ShortListprep2];
return ShortList;
}
- (NSMutableArray*) shuffleCards{
NSUInteger cardCount = [self.cardImages count];
NSMutableArray *count = [[NSMutableArray alloc]init];
for (int tileID = 0; tileID < cardCount; tileID++){
[count addObject:[NSNumber
numberWithInt:tileID]];
}
return count;
}
Then I call this method from the view controller and it all works.
Next, I would like to add a different list of images so that different sets of images can be chosen when playing the game. Something like:
NSMutableArray *Clothes = [[NSMutableArray alloc] initWithObjects:
[UIImage imageNamed:#"Hat.png"],
[UIImage imageNamed:#"Coat.png"],
[UIImage imageNamed:#"Gloves.png"],
[UIImage imageNamed:#"Trousers.png"],
[UIImage imageNamed:#"Socks.png"],
[UIImage imageNamed:#"Sweater.png"],
[UIImage imageNamed:#"T-shirt.png"],
[UIImage imageNamed:#"Shirt.png"],
[UIImage imageNamed:#"Jumper.png"],
[UIImage imageNamed:#"Underwear.png"],
nil];
I know I could just duplicate the whole method for each new set, something like this:
- (NSMutableArray*) createCardImagesClothes{
NSMutableArray *Clothes = [[NSMutableArray alloc] initWithObjects:
[UIImage imageNamed:#"Hat.png"],
[UIImage imageNamed:#"Coat.png"],
[UIImage imageNamed:#"Gloves.png"],
[UIImage imageNamed:#"Trousers.png"],
[UIImage imageNamed:#"Socks.png"],
[UIImage imageNamed:#"Sweater.png"],
[UIImage imageNamed:#"T-shirt.png"],
[UIImage imageNamed:#"Shirt.png"],
[UIImage imageNamed:#"Jumper.png"],
[UIImage imageNamed:#"Underwear.png"],
nil];
NSUInteger tileCount = [FullList count];
for (NSUInteger i = 0; i < tileCount; ++i) {
NSInteger nElements = tileCount - i;
NSInteger n = (arc4random() % nElements) + i;
[FullList exchangeObjectAtIndex:i withObjectAtIndex:n];
}
NSArray *Shortlistprep1 = [FullList subarrayWithRange:NSMakeRange(0, 10)];
NSArray *ShortListprep2=[Shortlistprep1 arrayByAddingObjectsFromArray:Shortlistprep1];
NSMutableArray *ShortList = [[NSMutableArray alloc]init];
ShortList = [NSMutableArray arrayWithArray:ShortListprep2];
return ShortList;
}
And I am fine with doing this if this is the way forward, I just would like to learn best practices. I feel like there should be a way to just write one method and then have the properties declared on the h file as so:
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#interface Cards : NSObject
// Create Cards
#property NSMutableArray *cardImages;
#property NSMutableArray *shuffledCards;
#propery NSMutableArray *Fruits;
#propery NSMutableArray *Clothes;
#propery NSMutableArray *SomeOtherSet;
#propery NSMutableArray *SomeOtherSet;
#propery NSMutableArray *SomeOtherSet;
- (NSMutableArray*) createCardImages;
- (NSMutableArray*) shuffleCards;
#end
and then all I would have to do is modify my method to accept an argument of the type NSMutableArray, like so:
- (NSMutableArray*) createCardImages:(NSMutableArray);
And just call the method with the argument which would represent whatever set I want to load.
My question, and it's a fairly basic one, is where would I actually load information into those arrays. There is no "main" or "viewDidLoad" method on the "m" file to actually load the data -in this case the images- into the array.
Does that make sense?
Thanks for reading.
Arguably you shouldn't be exposing the mutable array, your model should be a better container for the data and actually operate on the data. Shuffling by returning an array of numbers doesn't actually change the data model and it should.
Also, it would be cleaner if your data model dealt with data management, not the explicit creation. If you had a factory class which could be asked to generate a set of images and supply those images for your Cards class to deal with you would separate your concerns better. You could add class methods to Cards, or an instance method which took an enum to decide which images to use, but the images are really a separate issue to the management of those images as cards.
So, the view controller, when it's created to manage a game, asks the factory class for an instance of Cards for a specified image type. Once it has that Cards instance it can shuffle it and then request the card at a specified index, or grid location.
Index and grid location are interesting parts, because that could be different for different game types. Different game types can all use the same set of cards (though with a different Cards instance), but index those cards in a different way. To support this it's good to offer a simple index into the cards and allow the controller which knows the game logic to decide how to use that index system.
In all of the above the controller never accesses the cards as an array. The interface is similar, but all interaction is with Cards and that class hides its internal structure. Internally it would use an array, but it could use something else and the controller shouldn't care.
My while loop doesn't seem to work. When loading this view, the app freezes.
When I delete the part of code, containing the while loop, the app won't freeze.
What I'm searching for is a piece of code that will cause that the same array is not chosen twice.
#interface ThirdViewController ()
#end
#implementation ThirdViewController
...
NSString * Answer = #"";
NSArray * RAMArray;
...
- (void)NewQuestion
{
NSString * PlistString = [[NSBundle mainBundle] pathForResource:#"Questions" ofType:#"plist"];
NSMutableArray * PlistArray = [[NSMutableArray alloc]initWithContentsOfFile:PlistString];
NSArray *PlistRandom = [PlistArray objectAtIndex: random()%[PlistArray count]];
while (![PlistRandom isEqual: RAMArray])
{
NSArray *PlistRandom = [PlistArray objectAtIndex: random()%[PlistArray count]];
}
RAMArray = PlistRandom;
...
}
- (void)Check:(NSString*)Choise
{
...
if ([Choise isEqualToString: Answer])
{
...
[self NewQuestion];
}
}
- (IBAction)AnsButA:(id)sender
{
UIButton *ResultButton = (UIButton *)sender;
NSString *Click = ResultButton.currentTitle;
[self Check:Click];
}
My guess is that because you re-declare PlistRandom within the while loop, the inner-declared variable may be out of scope at the point the while conditionis evaluated. Your problem I think is a scoping issue, just change the loop to this and see if that works:
while (![PlistRandom isEqual: RAMArray])
{
PlistRandom = [PlistArray objectAtIndex: random()%[PlistArray count]];
}
I am setting the array for a series of views with the following code. bunnyView1 through bunnyView7 are instances of UIImageView created in the storyboard but I want to automate the process so I can generate the connections with code. How would I set the values inside a loop?
- (void)viewDidLoad
{
NSMutableArray *hopAnimation = [[NSMutableArray alloc] initWithCapacity:20];
for (int i = 1; i <= 20; i++) {
[hopAnimation addObject:[UIImage imageNamed:[NSString stringWithFormat:#"frame-%i.png", i]]];
}
self.bunnyView1.animationImages=hopAnimation;
self.bunnyView2.animationImages=hopAnimation;
self.bunnyView3.animationImages=hopAnimation;
self.bunnyView4.animationImages=hopAnimation;
self.bunnyView5.animationImages=hopAnimation;
self.bunnyView6.animationImages=hopAnimation;
self.bunnyView7.animationImages=hopAnimation;
self.bunnyView1.animationDuration=1;
self.bunnyView2.animationDuration=1;
self.bunnyView3.animationDuration=1;
self.bunnyView4.animationDuration=1;
self.bunnyView5.animationDuration=1;
self.bunnyView6.animationDuration=1;
self.bunnyView7.animationDuration=1;
[super viewDidLoad];
}
Add your variables in an Array. And iterate the array and set values . For ex,
NSArray *collection = [[NSArray alloc]initWithObjects:#"object1",#"object2",#"object3",#"object4",nil];
You should use an IBOutletCollection of UIImageView which every bunnyView should be linked to. Declaration goes like this:
#property (nonatomic,retain) IBOutletCollection(UIImageView) NSArray * bunnyViews;
I'm creating UILabels dynamically in a for each loop. Every loop that is run creates 1-4 UILabels.
What I want is that I put these UILabels into my NSMutableArray and being able later to easy retrieve the data.
My original thought was to put these UILabels into a NSDictionary and use [dictGroupLabels setValue:uiLabel1 forKey:#"uiLabel1"] and then [dictGroupLabels setValue:uiLabel2 forKey:#"uiLabel2"] and so on. And then put this dictionary into my NSMutableArray for each loop. Later on I could access the values like UILabel *label = [[myArray objectAtIndex:0] valueForKey:#"uiLabel1"] BUT that unfortunately doesn't work since UILabels don't conform to the NSCopying protocol.
So with this in mind how would you solve this?
this question provided more information on what you are trying to accomplish. Since you know for a fact, the possible set of labels you are trying to create in each case, I would highly recommend using mutable dictionaries instead of arrays.
To illustrate, given the following hypothetical class definition:
#interface MyClass: NSObject {
NSMutableDictionary * _labelDict;
}
#property (nonatomic, retain) NSMutableDictionary * labelDict;
- ( void )methodA;
- ( void )methodB;
- (NSMutableDictionary *) labelsForRunLoop: (NSUInteger) loopIdx;
#end
You would have the following, hypothetical, class implementation:
#implementation MyClass
#synthesize labelDict = _labelDict;
- ( id ) init {
if( ( self = [ super init ] ) ) {
[self setLabelDict: [NSMutableDictionary dictionaryWithCapacity: 8]];
}
}
- ( void ) dealloc {
[ self.labelDict release ];
[ super dealloc ];
}
- ( void ) methodA {
for(NSUInteger i = 0; i < some index; i++) {
[self.labelDict setObject: [self labelsForRunLoop: i] forKey: [NSString stringWithFormat: #"%d", i]];
}
}
- ( void ) methodB {
// Locate the label you need to work with. Example based on this crude pseudo code
NSMutableDictionary * subDict = (NSMutableDictionary *) [self.labelDict objectForKey: #"0"];
UILabel * theLabel = (UILabel * ) [subDict objectForKey: #"UILabel.Z"];
theLabel.text = #"Label 1";
}
- (NSMutableDictionary *) labelsForRunLoop: (NSUInteger) loopIdx {
NSMutableDictionary * dictionary = [NSMutableDictionary dictionaryWithCapacity: 4] ;
[dictionary setObject: create-w-label forKey: #"UILabel.W"];
[dictionary setObject: create-x-label forKey: #"UILabel.X"];
[dictionary setObject: create-y-label forKey: #"UILabel.Y"];
[dictionary setObject: create-z-label forKey: #"UILabel.Z"];
return [dictionary retain];
}
#end
This is basically pseudo code and will not successfully compile. However it will serve as a good starting point. You probably want to store each label dictionary under some key that makes sense, instead of just using the loop's index. Hope this helps.
They don’t need to adhere to NSCopying to be added to an array. It sounds like you just need to do something like this:
NSMutableArray *mainArray = [NSMutableArray array];
for(int i = 0; i < 5; i++)
{
NSMutableArray *subArray = [[NSMutableArray alloc] initWithCapacity:5];
for(int j = 0; j < 4; j++)
{
UILabel *label = [[UILabel alloc] init];
// etc.
[subArray addObject:label];
[label release];
}
[mainArray addObject:subArray];
[subArray release];
}
// then, to get one of the labels:
UILabel *someSpecificLabel = [[mainArray objectAtIndex:2] objectAtIndex:1];
I want update position of Particles for store this information I use NSMutableArray. I want do update 1 time per 2 seconds for example. For this I try use this code.
//ParticlesLayer.h
#interface ParticlesLayer : CCLayer {
CCQuadParticleSystem *particle;
NSMutableArray *partilesCoordinates;
}
#property (assign, readwrite) NSMutableArray *partilesCoordinates;
-(void)partilesMove:(ccTime)dt;
-(void)particleInit;
//ParticlesLayer.m
#implementation ParticlesLayer
#synthesize partilesCoordinates;
-(id) init {
self = [super init];
partilesCoordinates = [NSMutableArray arrayWithCapacity:5];
[partilesCoordinates addObject: [NSValue valueWithCGPoint:ccp(67,152)]];
[partilesCoordinates addObject: [NSValue valueWithCGPoint:ccp(140,147)]];
[partilesCoordinates addObject: [NSValue valueWithCGPoint:ccp(198,100)]];
[partilesCoordinates addObject: [NSValue valueWithCGPoint:ccp(261,126)]];
[partilesCoordinates addObject: [NSValue valueWithCGPoint:ccp(364,135)]];
// bla bla bla
//...
//bla bla bla
[self schedule:#selector(partilesMove:) interval:2];
return self;
}
-(void)emittersMove:(ccTime)dt{
NSMutableArray *newParticlesCoordinates = [NSMutableArray arrayWithCapacity:5];
CGPoint newPoint;
for (int i = 0; i < 5; ++i) {
[self particleInit];
NSLog(#"%#", [particlesCoordinates objectAtIndex:i]); // on this part of code i have problem
particle.position = [[particlesCoordinates objectAtIndex:i] CGPointValue];
[self addChild:emitter];
newPoint = emitter.position;
newPoint.x += 15;
newPoint.y += 15;
[newParticlesCoordinates addObject:[NSValue valueWithCGPoint:newPoint]];
}
[partilesCoordinates removeAllObjects];
[partilesCoordinates newParticlesCoordinates];
[newParticlesCoordinates removeAllObjects];
}
But when i run it on first calling function and reading from particlesCoordinates array i have console message : Program received signal: "EXC_BAD_ACCESS". How I can make normal-working schedule?
[NSMutableArray arrayWithCapacity:5] returns an autoreleased instance, and that gets cleaned up some time after leaving the initializer. Now the pointer partilesCoordinates points to undefined memory. You need to do:
partilesCoordinates = [[NSMutableArray arrayWithCapacity:5] retain];
or
partilesCoordinates = [[NSMutableArray alloc] initWithCapacity:5];
Change this:
#property (assign, readwrite) NSMutableArray *partilesCoordinates;
to this:
#property (nonatomic, retain) NSMutableArray *partilesCoordinates;
and change this:
partilesCoordinates = [NSMutableArray arrayWithCapacity:5];
to:
self.partilesCoordinates = [NSMutableArray arrayWithCapacity:5];
When you synthesize a setter for an "assign" property, the accessor won't retain the value you pass it.