nsarray switching image texture randomly everyone one second - iphone

I have an array that has 2 balloon textures,one green and one red.I have 12 green balloon object at the start of the app.Basically I want one random green balloon to turn red every one second.If anyone can help it would be appreciated.
init method
balloonTextures = [NSMutableArray array];
[balloonTextures addObject:[SPTexture textureWithContentsOfFile:#"greenballoon.png"]];
[balloonTextures addObject:[SPTexture textureWithContentsOfFile:#"redballoon.png"]];
[balloonTextures retain];
playFieldSprite = [SPSprite sprite];
[self addChild:playFieldSprite];
[self addBalloon];
add ballon method
-(void)addBalloon
{
for(int i = 0; i < 12; i++)
{
SPImage *image = [SPImage imageWithTexture:[balloonTextures objectAtIndex:0]];
image.x = 40*i;
image.y = 10 ;
[playFieldSprite addChild:image];
}
}

For getting random number:
#include <stdlib.h>
int r = arc4random() % 12;
Use a NSTimer to be called every second: How do I use NSTimer?

You could use CADisplayLink instead of NSTimer. The reason is that CADisplayLink is synchronized its drawing to the refresh rate of the display.
How to setup CADisplayLink:
id displayLink = [NSClassFromString(#"CADisplayLink") displayLinkWithTarget:self selector:#selector(changeBalloonTexture)];
[displayLink setFrameInterval:60]; // refresh rate is 60fps, 1=60fps, 60=1fps
[displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
How to check whether CADisplayLink is supported or not on the particular device:
// code from cocos2d
displayLinkSupported = FALSE;
NSString *reqSysVer = #"3.1";
NSString *currSysVer = [[UIDevice currentDevice] systemVersion];
if ([currSysVer compare:reqSysVer options:NSNumericSearch] != NSOrderedAscending)
displayLinkSupported = TRUE;

For this you'll need to keep track of Green Balloons & change one of them to red randomly.
Put all your balloon sprites in one NSArray say balloonArray & schedule a timer to run a method every second. In that method, iterate the balloonArray, & collect all green balloons into another array say greenBalloonsArray.
Then use arch4random() method to generate a random number (index) from 0 to the length of greenBalloonsArray. Use random number as an index of newly created greenBalloonsArray to fetch the balloonSprite and change it's texture to red.
// Sudo Code
// Add all objects to one array
NSMutableArray *balloonsArray = [[NSMutableArray alloc] initWithObjects: ..... ];
- (void) timerMethod
{
__block NSMutableArray *greenBalloonsArray = [[NSMutableArray alloc] init];
[balloonsArray enumerateObjectsUsingBlock:^(BalloonSprite *object, NSUInteger idx, BOOL *stop) {
if (object.isGreen)
[greenBalloonsArray addObject:object];
}];
int index = arc4random() % greenBalloonsArray.count;
GreenBallonArray * greenBalloon = [greenBalloonsArray objectAtIndex:index];
[greenBallon turnToRed];
}

Related

Create multiple MKOverlays of polyline from locations coming via web-service

My app is real-time tracker, where multiple users are logged in and updating their location by sending their co-ordinates to our web service which is then called back let's after every 2 minutes to show all the users on my MapView.
Every time I get the locations of users from web service in connectionDidFinishLoading method, I am parsing, creating polyline through pointsArray and adding them to overlay:
-(void) connectionDidFinishLoading: (NSURLConnection *) connection
{
userLatitudeArray = [[NSMutableArray alloc]init];
userLongitudeArray = [[NSMutableArray alloc]init];
userIdArray = [[NSMutableArray alloc]init];
userNameArray = [[NSMutableArray alloc]init];
userProfilePicArray = [[NSMutableArray alloc]init];
profilePicURLStringArray = [[NSMutableArray alloc]init];
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
NSArray *trackingDict = [NSJSONSerialization JSONObjectWithData:empJsonData options:kNilOptions error:nil];
if ([trackingDict count] >= 2) {
for (trackUsersCount = 0; trackUsersCount< trackingDict.count; trackUsersCount++) {
NSLog(#"trackUsersCount %i", trackUsersCount);
NSMutableArray *latlongArray = [[NSMutableArray alloc]init];
latlongArray = [[trackingDict objectAtIndex:trackUsersCount]objectForKey:#"latlong"];
[userLongitudeArray removeAllObjects];
[userLatitudeArray removeAllObjects];
for (int i = 0; i<latlongArray.count; i++) {
[userLatitudeArray addObject:[[latlongArray objectAtIndex:i]objectForKey:#"lat"]];
[userLongitudeArray addObject:[[latlongArray objectAtIndex:i]objectForKey:#"long"]];
}
NSString *name = [[trackingDict objectAtIndex:trackUsersCount]objectForKey:#"user_firstName"];
// ProfilePIC URL
profilePicURLString = [[trackingDict objectAtIndex:trackUsersCount]objectForKey:#"user_profilePicture"];
[userNameArray addObject:name];
[profilePicURLStringArray addObject:profilePicURLString];
int i;
if (userLatitudeArray.count>1) {
for (i = 0; i<userLatitudeArray.count; i++) {
CLLocationCoordinate2D userLocation;
userLocation.latitude = [[userLatitudeArray objectAtIndex:i]doubleValue];
userLocation.longitude = [[userLongitudeArray objectAtIndex:i] doubleValue];
MKMapPoint * pointsArray = malloc(sizeof(CLLocationCoordinate2D)*userLongitudeArray.count);
pointsArray[i] = MKMapPointForCoordinate(userLocation);
polyline = [MKPolyline polylineWithPoints:pointsArray count:i];
free(pointsArray);
}
polyline.title = name;
[mapView addOverlay:polyline];
}
}
}
}
What I want to do is to have control on each polyline created for each user, so I can change the color of it and hide/show them on click of a button (one to show/hide my track and the other for all other users), this is why I am adding title to it.
I can see now that I am adding polyline to the same overlay, which is wrong I believe. But I don't know how many users will be there in web-service so can add multiple overlays of them.
Initially I thought I am able to remove a particular polyline with title but then I realised it is removing all as polyline.title property gets updated.
Any help would be much appreciated!
You could collect an array of those tracks that relate to other users, and keep a single track for the current user. If you clean the array at the start of the connectionDidFinishLoading function and populate it where you are currently adding the overlays to the map, then you move the addOverlay to a new function that you call at the end.
- (void) resetMap
{
if (showOtherTracks)
{
[mapView addOverlays:otherUserTracks];
} else {
[mapView removeOverlays:otherUserTracks];
}
if (showMyTrack)
{
[mapView addOverlay:myTrack];
} else {
[mapView removeOverlay:myTrack];
}
}
You can also call this when ever the button is pressed and the state changes.

Collection was mutated while being enumerated when [mapView addOverlay:overlayPolygon];

i tired to look over SF for solution, but can't find a solution.
maybe i missed it please help.
i tried to check out a user pinpoint with polygon by looping through all the KMLs.
the app always crash # this point:
[mapView addOverlay:overlayPolygon];
// zoom the map to the polygon bounds
[mapView setVisibleMapRect:overlayPolygon.boundingMapRect animated:YES];
problem code:
//create KML in hidden Mapview
-(void)loadKML:(NSMutableArray *)kmlNameArray
{
//dispatch_group_t group = dispatch_group_create();
//remove polygon and redraw again.
[NSThread detachNewThreadSelector: #selector(spinEnd) toTarget:self withObject:nil];
[mapView removeOverlays:mapView.overlays];
[inUserRangeArray removeAllObjects];
[inUserRangeArrayObjectIndex removeAllObjects];
[scrollview removeFromSuperview];
[pageControl removeFromSuperview];
[NSThread detachNewThreadSelector: #selector(spinBegin) toTarget:self withObject:nil];
NSArray *sysPaths = NSSearchPathForDirectoriesInDomains( NSDocumentDirectory, NSUserDomainMask, YES );
NSString *docDirectory = [sysPaths objectAtIndex:0];
for (int e=0; e<[kmlNameArray count]; e++)
{
//NSString *kmlNameStr = [kmlNameArray objectAtIndex:e];
Frog *kmlID = [self.fs objectAtIndex:e];
self.kmlID = [NSString stringWithFormat:#"%i",kmlID.fID];
self.kmlIDObjectIndex = [NSString stringWithFormat:#"%i",e];
NSLog(#"asasas %#",kmlIDObjectIndex);
//NSLog(#"KML items %#", kmlNameStr);
//NSLog(#"KML ID %#", kmlID);
//NSLog(#"KML file Path %#",[NSString stringWithFormat:#"%#/data/%#/%#", docDirectory,self.kmlID,[kmlNameArray objectAtIndex:e]]);
SimpleKML *kml = [SimpleKML KMLWithContentsOfFile:[NSString stringWithFormat:#"%#/data/%#/%#", docDirectory,self.kmlID,[kmlNameArray objectAtIndex:e]]error:NULL];
// look for a document feature in it per the KML spec
// dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
if (kml.feature && [kml.feature isKindOfClass:[SimpleKMLDocument class]])
{// see if the document has features of its own
for (SimpleKMLFeature *feature in ((SimpleKMLContainer *)kml.feature).features)
{// otherwise, see if we have any placemark features with a polygon
if ([feature isKindOfClass:[SimpleKMLPlacemark class]] && ((SimpleKMLPlacemark *)feature).polygon)
{
SimpleKMLPolygon *polygon = (SimpleKMLPolygon *)((SimpleKMLPlacemark *)feature).polygon;
SimpleKMLLinearRing *outerRing = polygon.outerBoundary;
//points[i], i = number of coordinates
CLLocationCoordinate2D points[[outerRing.coordinates count]];
NSUInteger i = 0;
for (CLLocation *coordinate in outerRing.coordinates)
{
points[i++] = coordinate.coordinate;
}
// create a polygon annotation for it
self.overlayPolygon = [MKPolygon polygonWithCoordinates:points count:[outerRing.coordinates count]];
//crash here
[mapView addOverlay:overlayPolygon];
// zoom the map to the polygon bounds
[mapView setVisibleMapRect:overlayPolygon.boundingMapRect animated:YES];
}
}
}
}
Before you loop over the array, you can create a new array with the elements. So when the original looped array is mutated (either by you, or by its owner) the array you loop over stays intact.
NSArray *theFeatures = [NSArray arrayWithObjects:((SimpleKMLContainer *)kml.feature).features];
for (SimpleKMLFeature *feature in theFeatures) {
}
So in case of looping over the SimpleKMLContainer features directly, you create a temporary new array with those features, and loop over that array.
Because you experience a crash on addOverlay: you must somehow be looping over the entire overlay collection. I don't see that directly in your code, so I assume that the features collection is somehow tied to the map kit overlays.
Another thing you could is not use the fast enumeration version of the for. So try replacing this:
for (SimpleKMLFeature *feature in ((SimpleKMLContainer *)kml.feature).features)
{
// Your code here
}
with this:
for (NSInteger index = 0; index < [((SimpleKMLContainer *)kml.feature).features count]; index++)
{
SimpleKMLFeature *feature = [((SimpleKMLContainer *)kml.feature).features objectAtIndex:index];
// Your code here
}
Let me know if that solves your problem.
better check out the sample code called HazardMap to see how to implement.
I just resolved the same issue by defining the Array I was enumerating as a normal NSArray instead of a NSMutableArray.
That did the trick for me.

problem with collision of UIImageViews

-(void)moveTheImage{
for (NSUInteger i = 0; i < [views count]; i++) {
image = [views objectAtIndex:i];
X = [[XArray objectAtIndex:i] floatValue];
Y = [[YArray objectAtIndex:i] floatValue];
image.center=CGPointMake(image.center.x + X, image.center.y + Y);
if(!intersectFlag)
{
if(CGRectIntersectsRect(image.frame,centre.frame))
{
intersectFlag = YES;
label.text= [NSString stringWithFormat:#"%d", count];
++count;
}
}
else
{
if(!CGRectIntersectsRect(image.frame,centre.frame))
{
intersectFlag = NO;
}
}
}
I would like that every time "image" intersect with "centre" the count increase of 1 but when there is a collision the count grows very fast until "image" doesn't touch "centre". More precisely "image" moves an then pass through "centre" and the count grows and when "image" is not in contact with "centre" the counter stops.How cn I solve this please? sorry for my english I'm french :/
As I said on the last comment, I think your count problem comes because of the frame rate.
So you can use an NSMutableSet which will contain all the objects being in collision.
In the interface, you declare your new set:
NSMutableSet * collisionObjects;
In the init method:
collisionObjects = [[NSMutableSet alloc] init];
In the dealloc:
[collisionObjects release];
And your method becomes:
-(void)moveTheImage{
for (NSUInteger i = 0; i < [views count]; i++) {
image = [views objectAtIndex:i];
X = [[XArray objectAtIndex:i] floatValue];
Y = [[YArray objectAtIndex:i] floatValue];
image.center=CGPointMake(image.center.x + X, image.center.y + Y);
if (CGRectIntersectsRect(image.frame,centre.frame)) {
// If the object is in collision, we add it to our set
// If it was already in it, it does nothing
[collisionObjects addObject:image];
}
else {
// If the object is not in collision we remove it,
// just in case it was in collision last time.
// If it was not, it does nothing.
[collisionObjects removeObject:image];
}
}
// The intersectFlag is updated : YES if the set is empty, NO otherwise.
intersectFlag = [collisionObjects count] > 0;
// We display the count of objects in collision
label.text= [NSString stringWithFormat:#"%d", [collisionObjects count]];
}
And this way you always have the count of your objects in collision using [collisionObjects count].

Game Center multiplayer code

As per the docs, I'm using:
- (void) match:(GKMatch*) match
player:(NSString*) playerID
didChangeState:(GKPlayerConnectionState) state;
to carry out the initial game negotiation. I do this in the scope of:
if (matchStarted_ == NO && [match expectedPlayerCount] == 0) { ... }
I need to decide which device is responsible for setting up the game. I do this by sorting the match.playerIDs NSArray instance and comparing the [GKLocalPlayer localPlayer].playerID NSString to the playerID NSString at index 0 of the sorted array. This player creates the game, sending out the data to all players.
However, and even with an expectedPlayerCount of 0, the playerIDs array has zero entries at this point, giving me an array overrun. Why is that? And what should I do instead to make a well-defined choice of player to generate the game?
For the decision code, take a look at the GKTank example provided by Apple - they take a hash of the device id, send it to the other client, and whichever has the lower number is the "host". That seems like a pretty solid way to decide.
here is sample code to do that thing
NSString *uid = [[UIDevice currentDevice] uniqueIdentifier];
CoinTossID = [uid hash];
now in delegate Function
- (void)match:(GKMatch *)match didReceiveData:(NSData *)data fromPlayer:(NSString *)playerID
{
NSMutableArray *ReceivedArray = [[NSMutableArray alloc] init];
ReceivedArray = [NSKeyedUnarchiver unarchiveObjectWithData:data];
int flag = [[ReceivedArray objectAtIndex:0] intValue];
[ReceivedArray removeObjectAtIndex:0];
int CoinValue = [ReceivedCoinTossID intValue];
if(CoinValue > CoinTossID)
{
isPlayer1 = YES;
}
else
{
isPlayer1 = NO;
}
}

Adding large numbers of properties in Core Data, crashing when starting from phone but not from Xcode

I am trying to add data to CoreData. It works fine when I build from Xcode to the phone but when I try to start the app directly from iPhone it crashes on first save to the Context.
I read a text file that is synced via iTunes File Sharing, the file is pretty big (~350 000 lines). The values I get from the file is added to two different arrays (barcodes and productNames). The arrays are later batched through and the sent to the function where I save the data.
From the array loop:
[...]
words = [rawText componentsSeparatedByString:#";"];
int loopCounter = 0;
int loopLimit = 20000;
int n = 0;
int wordType;
NSEnumerator *word = [words objectEnumerator];
NSLog(#"Create arrays");
while(tmpWord = [word nextObject]) {
if ([tmpWord isEqualToString: #""] || [tmpWord isEqualToString: #"\r\n"]) {
// NSLog(#"%#*** NOTHING *** ",tmpWord);
}else {
n++;
wordType = n%2;
if (wordType == kBarcode) {
[barcodes addObject: tmpWord];
}else if (wordType == kProduct) {
[productNames addObject: tmpWord];
}
// Send to batch //
loopCounter ++;
if (loopCounter == loopLimit) {
loopCounter = 0;
NSLog(#"adding new batch");
[self addBatchOfData];
[barcodes release];
[productNames release];
barcodes = [[NSMutableArray arrayWithCapacity:20000] retain];
productNames = [[NSMutableArray arrayWithCapacity:20000] retain];
}
}
[...]
And then the save-function:
-(void)addBatchOfData {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSError *error;
NSUInteger loopLimit = 5000;
NSUInteger loopCounter = 0;
NSString *ean;
NSString *designation;
for (int i=0; i<[barcodes count];i++ ) {
ean = [barcodes objectAtIndex:i];
designation = [productNames objectAtIndex:i];
Product *product = (Product *)[NSEntityDescription insertNewObjectForEntityForName:#"Product" inManagedObjectContext:importContext];
[product setDesignation:designation];
[product setBarcode:ean];
loopCounter ++;
if (loopCounter == loopLimit) {
NSLog(#"Save CoreData");
[importContext save:&error];
[importContext reset];
[pool drain];
pool = [[NSAutoreleasePool alloc] init];
loopCounter = 0;
}
}
// Save any remaining records
if (loopCounter != 0) {
[importContext save:&error];
[importContext reset];
}
[pool drain];
}
It's really irritating that it works fine when I build from Xcode. Hopefully there is a setting that I missed or something...
EDIT: Forgot to mention that I don't get passed the Default-screen and I don't have any logs. Can it have something to do with the provisioning?
Offload your file loading in a background thread and let the phone start up your main window and view. iOS will kill your app if you do not present a view in a timely manor (this is what you are seeing).
I have to do something like this for my xml -> CoreData converter code. I just present the user with a view notifying them of what is going on and a progress bar (I use https://github.com/matej/MBProgressHUD).
something like:
self.hud = [[MBProgressHUD alloc] initWithView:window];
// Set determinate mode
hud.mode = MBProgressHUDModeDeterminate;
hud.delegate = self;
hud.labelText = #"Converting Data File";
[self.window addSubview:hud];
// Show the HUD while the provided method executes in a new thread
[hud showWhileExecuting:#selector(convertToCoreDataStoreTask) onTarget:self withObject:nil animated:YES];
You just have to make sure that you use a separate NSManagedObjectContext in the new thread.
I would suggest that you implement this delegate method and then try to see what is going on with memory.
when running in the simulator, you have no memory constraints, but when running in the phone you do
- (void)applicationDidReceiveMemoryWarning:(UIApplication *)application
{
}
I think I find the solution to my question.
What I was doing was that I started all the heavy data crunch in the "- (void) viewDidLoad {". When I changed it to start the crunch after I clicked a button in the app, it worked just fine.
Right now it's just finding out where the start the data crunch, any suggestions?