I am creating an app that will show how a bubble sort works. The one problem I am having is the animation. It seems to be skipping and the animation is firing all at once. Within my if statement I want to swap the two images and animate the swap. Then I want a delay so once they swap it will go back through the loop and swap the next two images. Below is my code. I have tried many things like sleep() but it did not seems to work. The images are four boxes and I'm swapping them by left to right, smallest to largest.
for (int i = 0; i < 3; i++)
{
UIImageView *temp1 = [imagesArray objectAtIndex:i];
UIImageView *temp2 = [imagesArray objectAtIndex:i+1];
temp1Width = temp1.frame.size.width;
temp2Width = temp2.frame.size.width;
if (temp1Width > temp2Width)
{
CGPoint tempPoint = temp1.center;
[UIView animateWithDuration:1.0
delay: 1.0
options: nil
animations:^{
temp1.center = temp2.center;
}
completion:^(BOOL finished){
[UIView animateWithDuration:1.0
delay: 0.0
options: nil
animations:^{
temp2.center = tempPoint;
}
completion:^(BOOL finished){
}];
}];
[imagesArray exchangeObjectAtIndex:i+1 withObjectAtIndex:i];
}
}
Any Help would be appreciated.
If you apply an animation to a view that's already being animated, the new animation replaces the existing animation. It doesn't just get tacked on to the end. That's why your version doesn't work the way you want.
This happens even if you set a delay on the new animation. The existing animation will be removed, and the new animation will start after the specified delay. This prevents k20's simple answer from working. (Too bad, because that would be a really easy solution.)
So, a very simple alternative is to defer adding each additional animation at all. This is pretty easy with the dispatch_after function. First, let's factor out the code that swaps the views' Y origins into a helper function:
static void swapViewYOrigins(UIView *a, UIView *b) {
CGRect aFrame = a.frame;
CGRect bFrame = b.frame;
CGFloat t = aFrame.origin.y;
aFrame.origin.y = bFrame.origin.y;
bFrame.origin.y = t;
a.frame = aFrame;
b.frame = bFrame;
}
Now we can write the sort function, making it use dispatch_after to postpone each successive animation:
- (IBAction)sortButtonWasTapped {
NSTimeInterval duration = 1;
dispatch_time_t time = DISPATCH_TIME_NOW;
for (int i = imagesArray.count - 1; i > 0; --i) {
for (int j = 0; j < i; ++j) {
UIView *a = imagesArray[j];
UIView *b = imagesArray[j+1];
if (a.frame.size.width > b.frame.size.width) {
imagesArray[j] = b;
imagesArray[j+1] = a;
dispatch_after(time, dispatch_get_main_queue(), ^{
[UIView animateWithDuration:duration delay:0 options:UIViewAnimationOptionBeginFromCurrentState animations:^{
swapViewYOrigins(a, b);
} completion:nil];
});
time = dispatch_time(time, duration * NSEC_PER_SEC);
}
}
}
}
So this is an ok solution, but it does have one shortcoming. It's not easy to add a button that cancels the animations, because there's no API that removes a pending dispatch_after block from the queue.
If you want to support cancelation, it's better to explicitly manage the queue of pending operations. We can do that by adding an instance variable to hold the queue:
#implementation ViewController {
IBOutletCollection(UIView) NSMutableArray *imagesArray;
NSMutableArray *pendingSwaps;
}
Then, we modify sortButtonWasTapped to add a block to the queue instead of calling dispatch_after, and we make it start running the queue after it finishes sorting:
- (IBAction)sortButtonWasTapped {
pendingSwaps = [NSMutableArray array];
for (int i = imagesArray.count - 1; i > 0; --i) {
for (int j = 0; j < i; ++j) {
UIView *a = imagesArray[j];
UIView *b = imagesArray[j+1];
if (a.frame.size.width > b.frame.size.width) {
imagesArray[j] = b;
imagesArray[j+1] = a;
[pendingSwaps addObject:^{
swapViewYOrigins(a, b);
}];
}
}
}
[self runPendingSwaps];
}
We're using the same swapViewYOrigins function as before. We run the pending swaps like this:
- (void)runPendingSwaps {
if (pendingSwaps.count == 0)
return;
void (^swapBlock)(void) = pendingSwaps[0];
[pendingSwaps removeObjectAtIndex:0];
[UIView animateWithDuration:1 animations:swapBlock completion:^(BOOL finished) {
[self runPendingSwaps];
}];
}
So, we just stop if the queue is empty. Otherwise, we take the first block off the queue, and we use it as the animation block of a UIView animation. We give the animation a completion block that calls runPendingSwaps again, which starts the next pending animation (if there is one) after the current one finishes.
To cancel, we can just set pendingSwaps to nil:
- (IBAction)cancelButtonWasTapped {
pendingSwaps = nil;
}
Note that if the user taps Cancel, the views will not necessarily be in the same order on the screen as they are in imagesArray, because imagesArray is fully sorted when sortButtonWasTapped returns. We can fix that by sorting imagesArray by Y origins (using the built-in sort) before starting the bubble sort:
- (IBAction)sortButtonWasTapped {
[self sortImagesArrayByY];
// etc. same as previous version
}
- (void)sortImagesArrayByY {
[imagesArray sortUsingComparator:^NSComparisonResult(id obj1, id obj2) {
CGFloat d = [obj1 frame].origin.y - [obj2 frame].origin.y;
return
d < 0 ? NSOrderedAscending
: d > 0 ? NSOrderedDescending
: NSOrderedSame;
}];
}
With this in place, you can click the Sort button, then click Cancel before the views have been fully sorted on screen, then click Sort again and it will resume (by performing bubble sort again).
Try doing it implicitly with a timer instead of using a loop and a delay. For example, you could use a scheduled NSTimer whose duration is equal to or greater than the animation time to fire this method:
- (void)performNextAnimation:(NSTimer *)timer
{
BOOL didSwap = NO;
for(NSInteger i = 0; i < [self.imageViews count] - 1; ++i)
{
UIImageView *image1 = [self.imageViews objectAtIndex:i];
UIImageView *image2 = [self.imageViews objectAtIndex:(i + 1)];
if(image1.frame.size.width <= image2.frame.size.width)
continue;
CGPoint center1 = image1.center;
CGPoint center2 = image2.center;
[UIView animateWithDuration:AnimationDuration
delay:0
options:UIViewAnimationOptionCurveEaseInOut
animations:^
{
image1.center = center2;
image2.center = center1;
}
completion:nil];
[self.imageViews exchangeObjectAtIndex:(i + 1) withObjectAtIndex:i];
didSwap = YES;
break;
}
if(!didSwap)
{
[self.animationTimer invalidate];
self.animationTimer = nil;
NSLog(#"Done!");
}
}
This simply finds the first two views that are out of order and swaps them, moving them to one another's center point simultaneously. You can, of course, choose any type of animation to perform for each out-of-order pair.
For completeness, here's how I set up my timer and views in the first place:
self.animationTimer = [NSTimer scheduledTimerWithTimeInterval:AnimationDuration
target:self
selector:#selector(performNextAnimation:)
userInfo:nil
repeats:YES];
CGFloat viewCenterX = self.view.center.x;
self.imageViews = [NSMutableArray array];
const CGFloat imageHeight = 35;
const CGFloat imageMargin = 10;
for(size_t i = 0; i < 10; ++i)
{
CGFloat imageWidth = arc4random_uniform(250) + 30;
CGRect imageFrame = CGRectMake(viewCenterX - imageWidth * 0.5, i * (imageHeight + imageMargin) + imageMargin,
imageWidth, imageHeight);
UIImageView *imageView = [[UIImageView alloc] initWithFrame:imageFrame];
imageView.backgroundColor = [UIColor redColor];
[self.view addSubview:imageView];
[self.imageViews addObject:imageView];
}
Even if you call the animateWithDuration:delay: different times, they are being called at almost the same time (the "for" time). As all of them have the same delay, you don't see the animation.
An easy fix would be using a dynamic delay:
[UIView animateWithDuration:AnimationDuration
delay:i
...
Related
I have a sprite kit game for Mac where I have multiple sprite nodes moving across the screen. How do I make it so I can removeFromParent a certain node?
For example, I have a loop that periodically adds a new node every second:
-(void)spawnNew {
xPos = 100;
yPos = 1000;
CGPoint location = CGPointMake(xPos, yPos);
orb = [SKSpriteNode spriteNodeWithImageNamed:#"orb"];
orb.position = location;
orb.scale = 0.3;
orb.name = #"orbSprite";
CGFloat dirX = 0;
CGFloat dirY = -30;
CGVector vector1 = CGVectorMake(dirX, dirY);
SKAction *action = [SKAction moveBy:vector1 duration:0.1];
[orb runAction:[SKAction repeatAction:action count:100000]];
[self addChild:orb];
}
How would I make it so that I can get the position or remove certain orbs, not the entire group? For example, if the position of a sprite is equal to x=100, y=200 then I would remove it from parent. Currently if I try to do this and check for an orb's position, it just checks the position of the newest orb to be added, not all of them which I would want.
Thanks, any help would be appreciated.
This might not be the most efficient answer but it does show how you might do this. keep in mind that generally with Sprite Kit your going to end up with a lot of nodes so grouping them under a parent SKNode is a good idea, it also lets to specify a point in the tree so you can easily get the children below it. The NSLog statements are just for testing/debug, I left then in as they may help if you try the code.
- (void)addOrbs {
SKNode *orbRoot = [SKNode node];
[orbRoot setName:#"ORB_ROOT"];
[self addChild:orbRoot];
for(int counter = 0; counter<5; counter++) {
SKSpriteNode *newOrb = [SKSpriteNode spriteNodeWithColor:[SKColor redColor] size:CGSizeMake(20, 20)];
[newOrb setPosition:CGPointMake(100, 100 + (100*counter))];
NSLog(#"ORB POS: %#", NSStringFromCGPoint([newOrb position]));
[newOrb setName:#"orbSprite"];
[orbRoot addChild:newOrb];
}
}
.
- (void)removeOrbAtPoint:(CGPoint)point {
SKNode *orbRoot = [self childNodeWithName:#"//ORB_ROOT"];
NSArray *allOrbs = [orbRoot children];
NSLog(#"ORBS: %#", allOrbs);
for(SKNode *eachOrb in allOrbs) {
if([eachOrb position].x == 100 && [eachOrb position].y == 200) {
NSLog(#"REMOVING: %# WITH POSITION: %#", [eachOrb name], NSStringFromCGPoint([eachOrb position]));
[eachOrb removeFromParent];
}
}
}
An alternate version of remove using fast block enumeration:
- (void)removeOrbAtPoint_v2:(CGPoint)point {
SKNode *orbRoot = [self childNodeWithName:#"//ORB_ROOT"];
NSArray *allOrbs = [orbRoot children];
NSLog(#"ORBS: %#", allOrbs);
[orbRoot enumerateChildNodesWithName:#"orbSprite" usingBlock:^(SKNode *node, BOOL *stop) {
if([node position].x == 100 && [node position].y == 200) {
NSLog(#"REMOVING: %# WITH POSITION: %#", [node name], NSStringFromCGPoint([node position]));
[node removeFromParent];
}
}];
}
A different approach - You can add the removeFromParent into an action sequence. This will automatically remove it when it gets to a defined point, with no additional code.
Would changing your moveBy: action to a moveTo: work?
SKAction *moveToFinish = [SKAction moveTo:finishPoint duration:5.0];
SKAction *removeMe = [SKAction removeFromParent];
SKAction *actionSeq = [SKAction sequence:#[moveToFinish,removeMe]];
[sprite runAction:actionSeq];
My finishPoint is generated by a random statement to give it some variety.
Loop through all the nodes in the node or scene you are adding the orbs to.
for(SKNode * child in self.children) {
...
}
See if they are within this box.
for(SKNode * child in self.children) {
if(CGRectIntersectsRect(child.calculateAccumulatedFrame, CGRectMake(10, 50, 100, 200))) {
...
}
}
Remove the child (CGRectMake takes x,y,width,height as parameters)
for(SKNode * child in self.children) {
if(CGRectIntersectsRect(child.calculateAccumulatedFrame, CGRectMake(10, 50, 100, 200))) {
[child removeFromParent];
}
}
I have Thrift server, I can get information and load them in my UITableView, at the first I load 10 rows, and after scrolling to the end each time I want to load 10 more rows(with information) but it's never work for me,
would you please help me in this implementation,
Thanks in advance!
Here is my numberOfRowsInSection method
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return _notes.count;
}
my method scrollViewDidScroll
-(void)scrollViewDidScroll: (UIScrollView*)scrollView
{
float scrollViewHeight = scrollView.frame.size.height;
float scrollContentSizeHeight = scrollView.contentSize.height;
float scrollOffset = scrollView.contentOffset.y;
if (scrollOffset == 1)
{
[self.tableView setContentOffset:CGPointMake(0, 44)];
}
else if (scrollOffset + scrollViewHeight == scrollContentSizeHeight)
{
// I don't know what should I put here
NSLog(#"%# we are at the end", _notes); //==> _notes is null
//we are at the end, it's in the log each time that scroll, is at the end
}
Here is how I connect to the server
- (void)viewDidAppear:(BOOL)animated{
[super viewDidAppear:YES];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,
(unsigned long)NULL), ^(void) {
NSURL *url = [NSURL URLWithString:BASE_URL];
THTTPClient *transport = [[THTTPClient alloc] initWithURL:url];
TBinaryProtocol *protocol = [[TBinaryProtocol alloc]
initWithTransport:transport
strictRead:YES
strictWrite:YES];
server = [[thrift_Client alloc] initWithProtocol:protocol];
_notes = [server get_notes:10 offset:0 sort_by:0 search_for:result];
__weak NotesTable *weakSelf = self;
dispatch_async(dispatch_get_main_queue(), ^{
[weakSelf.notesTable reloadData];
});
});
[self.view setNeedsDisplay];
}
Use SVPullToRefresh, it offers nice encapsulation. Add the library and register your UITableView with
[tableView addInfiniteScrollingWithActionHandler:^{
// prepend data to dataSource, insert cells at top of table view
// call [tableView.pullToRefreshView stopAnimating] when done
}];
Hi use this it works perfectly for me try this...
- (void)scrollViewDidScroll:(UIScrollView *)aScrollView
{
CGPoint offset = aScrollView.contentOffset;
CGRect bounds = aScrollView.bounds;
CGSize size = aScrollView.contentSize;
UIEdgeInsets inset = aScrollView.contentInset;
float y = offset.y + bounds.size.height - inset.bottom;
float h = size.height;
float reload_distance = 10;
if(y > h + reload_distance)
{
//Put your load more data method here...
}
}
}
You should put your request. The one that is executing on viewdidappear.. remove from viewdidappear put it inside a method. Then call this method from viewdidappear and from this place you put the log and instead of your implementation of didscroll this is one is better
- (void)scrollViewDidScroll: (UIScrollView *)scroll {
NSInteger currentOffset = scroll.contentOffset.y;
NSInteger maximumOffset = scroll.contentSize.height - scroll.frame.size.height;
// Change 10.0 to adjust the distance from bottom
if (maximumOffset - currentOffset <= 10.0) {
[self methodThatAddsDataAndReloadsTableView];
}
}
I am trying animation, where the UITableView is already in place,but the subview (which itself contains the cell's content view) for each of the UITableViewCells is outside the window bounds. It is outside the screen. When the view loads, the cell animate from left to right, one by one, till the rightmost x value of the frame touches the rightmost x value of the UITableView.
The animation is such that, once first cell takes off, the second cell takes off before the first cell is finished with it's animation. Similarly, the third cell takes off before the second is finished with it's animation. However, i am not able to achieve this, and i all my rows animate all-together.
-(void)animateStatusViewCells:(SupportTableViewCell *)cell
{
__block CGPoint center;
[UIView animateWithDuration:0.55 delay:0.55 options:UIViewAnimationOptionCurveEaseIn animations:^{
center = cell.statusView.center;
center.x += (cell.frame.size.width - 20);
cell.statusView.center = center;
}completion:^(BOOL success){
NSLog(#"Translation successful!!!");
double delayInSeconds = 2.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
//[self animateStatusViewCells:cell];
});
}];
}
-(void)animateSupportTableView:(UITableView *)myTableView
{
NSMutableArray *cells = [[NSMutableArray alloc] init];
NSInteger count = [myTableView numberOfRowsInSection:0];
for (int i = 0; i < count; i++) {
[cells addObject:[myTableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:i inSection:0]]];
}
for (SupportTableViewCell *cell in cells) {
[self animateStatusViewCells:cell];
}
}
-(void)viewDidAppear:(BOOL)animated{
[super viewDidAppear:animated];
[self animateSupportTableView:_statusTableView];
}
Here's the initWithStyle method for the custom UITableViewCell:
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
// Initialization code
_statusView = [[UIView alloc] initWithFrame:self.frame];
self.contentView.backgroundColor = [UIColor greenColor];
self.contentView.alpha = 0.7;
CGPoint cellCenter = self.center;
cellCenter.x = self.center.x - (self.frame.size.width - 20);
_statusView.center = cellCenter;
[self addSubview:_statusView];
[_statusView addSubview:self.contentView];
}
return self;
}
You need to use a different delay value for each animation. Instead of using a constant delay of .55, pass in the cell number to your animate method, and use something like:
CGFloat delay = .55 + cellNumber * cellDelay;
[UIView animateWithDuration: 0.55 delay: delay options:UIViewAnimationOptionCurveEaseIn animations:
^{
//animation code
}
];
For what its worth, this is a clean solution:
func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
let trans = CATransform3DMakeTranslation(-cell.frame.size.width, 0.0, 0.0)
cell.layer.transform = trans
UIView.beginAnimations("Move", context: nil)
UIView.setAnimationDuration(0.3)
UIView.setAnimationDelay(0.1 * Double(indexPath.row))
cell.layer.transform = CATransform3DIdentity
UIView.commitAnimations()
}
try this
declare this somewhere.
int c=0;
-(void)animateStatusViewCells:(SupportTableViewCell *)cell
{
__block CGPoint center;
[UIView animateWithDuration:0.55 delay:0.55 options:UIViewAnimationOptionCurveEaseIn animations:^{
center = cell.statusView.center;
center.x += (cell.frame.size.width - 20);
cell.statusView.center = center;
}completion:^(BOOL success){
c++;
if(c<[cells count]){
[self animateStatusViewCells:[cells objectAtIndex:c]];
}
}];
}
-(void)animateSupportTableView:(UITableView *)myTableView
{
NSMutableArray *cells = [[NSMutableArray alloc] init];
NSInteger count = [myTableView numberOfRowsInSection:0];
for (int i = 0; i < count; i++) {
[cells addObject:[myTableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:i inSection:0]]];
}
[self animateStatusViewCells:[cells objectAtIndex:c]];
}
I have a UILabe for my matching card game that writs "Good match!" for example if you have a match, and I want to make it disappear after notifying the user with the message.
What method do that? can I make it fad away..? thanks.
This is my updateUI method:
-(void) updateUI {
for (UIButton *cardButton in self.cardButtons) {
Card *card = [self.game cardAtIndex:[self.cardButtons indexOfObject:cardButton]];
[cardButton setTitle:card.contents forState:UIControlStateSelected];
[cardButton setTitle:card.contents forState:UIControlStateSelected|UIControlStateDisabled];
cardButton.selected = card.isFaceUp;
cardButton.enabled = !card.unplayble;
if (card.unplayble) {
cardButton.alpha = 0.1;
}
self.scoreCounter.text = [NSString stringWithFormat:#"Score: %d", self.game.score];
if (self.game.notification) {
[UIView animateWithDuration:2 animations:^{
self.notificationLabel.text = [NSString stringWithFormat:#"%#", self.game.notification];
self.notificationLabel.alpha = 0;
}];
}
}
}
This will fade away the label over two seconds. Change the animateWithDuration: to however long you want
if (self.game.notification == nil) {
[UIView animateWithDuration:2 animations:^{
self.notificationLabel.alpha = 0;
}];
}
If you want to change the text, try this (I can't test it right now, so it might not work)
[UIView animateWithDuration:2 animations:^{
self.notificationLabel.text = #"text you want to change it to";
}];
And then, when you want to use the label again, you have to set the self.notificationLabel.alpha back to 1. So try
if (self.game.notification) {
self.notificationLabel.alpha = 1;
[UIView animateWithDuration:2 animations:^{
self.notificationLabel.text = [NSString stringWithFormat:#"%#", self.game.notification];
self.notificationLabel.alpha = 0;
}];
}
I don't think that's possible. More than likely, you'll need to replace the UILabel with a NSView in Interface Builder and use the drawing functions contained therein to draw the text with transparency.
I've created a class method that returns a random position as an NSValue object.
I want to store the returned positions in a mutable array so the next time the class method gets called it checks the positions in the mutable array and if it finds one that is too close to the one stored it generates a new one.
What I can't seem to figure out is how to allocate and initialize my mutable array to use with my class method. I've tried allocating it in the class method. but that won't work because it will get initialized on each call therefor the previously stored values are... erased?
then I want to use the generated position to place a number of circles on the view (without them overlapping). right now the randomly generated positions work, but they overlap because of the mutable array not being implemented correctly in my randomposition class.
I'm still a beginner in objective c...and programming in general...
#implementation randomPosition
NSMutableArray *positionsArrayPlayer1;
+(NSValue *) givePosition:(int)player forRadius: (CGFloat) radius
{
if (player == 1)
{
//set range for arc4random
int fromNumberY = 550;
int toNumberY = 950;
//set range for arc4random
int fromNumberX = 0;
int toNumberX = 700;
BOOL far_enough_away = NO;
CGPoint newpoint;
while(!far_enough_away)
{
newpoint = CGPointMake((arc4random()%(toNumberX-fromNumberX+1))+fromNumberX,
(arc4random()%(toNumberY-fromNumberY+1))+fromNumberY);
far_enough_away = YES;
for(NSValue *existing in positionsArrayPlayer1)
{
NSLog(#"test");
CGPoint pointb = [existing CGPointValue];
CGFloat deltay = pointb.y-newpoint.y;
CGFloat deltax = pointb.x-newpoint.x;
CGFloat distance = sqrt(pow(deltax,2) + pow(deltay,2));
//fail if closer than desired radius
if(distance < radius )
{
far_enough_away = NO;
NSLog(#"test");
break;
}
[positionsArrayPlayer1 addObject:[NSValue valueWithCGPoint:newpoint]];
}
NSLog(#"%#",positionsArrayPlayer1);
}
return [NSValue valueWithCGPoint:newpoint];
} else if (player == 2 ){
//more stuff to come here....
} else {
NSLog(#"player invalid");
}
return nil;
}
here's the method I use in my viewcontroller. I also store the circle object that is placed on the view in a separate array for other manipulations.
- (void) positionCirclesPlayer1
{
radius = 70;
CGRect position;
for (int i = 0; i < [colors count];i++)
{
CGRect positionCircleInCenter = CGRectMake(self.view.frame.size.width/2-35, self.view.frame.size.height/2-35, radius, radius);
Circle *myCircle = [[Circle alloc] initWithFrame:positionCircleInCenter radius:radius color:[colors objectAtIndex:i]];
myCircle.label.text = [NSString stringWithFormat:#"%i", i];
myCircle.alpha = 0;
myCircle.userInteractionEnabled = NO;
theNewPointPlayer1 = [randomPosition givePosition:1 forRadius:radius];
position = CGRectMake(theNewPointPlayer1.CGPointValue.x, theNewPointPlayer1.CGPointValue.y, 70, 70);
[UIView animateWithDuration:0.4 delay:0 options:UIViewAnimationCurveEaseIn animations:^{
myCircle.frame = position; myCircle.alpha = 1;} completion:nil];
[playerOneCircles addObject:myCircle];
[self.view addSubview:myCircle];
}
}
Before learning Obj-C, you should probably learn something about C :)
+(NSValue*)givePosition:(int)player forRadius:(CGFloat)radius {
//this is executed when the method is called for the first time
static NSMutableArray *positionsArrayPlayer1 = nil;
if (positionsArrayPlayer1 == nil) { //checking if this is the first time execution
positionsArrayPlayer1 = [[NSMutableArray alloc] init];
}
}
or
//global variable accessible only from this file
static NSMutableArray *positionsArrayPlayer1;
#implementation randomPosition
//this method is called when the class is loaded.
+(void)initialize {
positionsArrayPlayer1 = [[NSMutableArray alloc] init];
}
+(NSValue*)givePosition:(int)player forRadius:(CGFloat)radius {
//do something with the array
}
#end
Note that implementing this functionality using class methods is probably not a very good architecture.