moving a ball in a grid xcode - iphone

I am trying to build an iphone app which has a grid of 5 columns up and down and 8 rows back and forth. I want to have a ball be able to traverse on these continuously. I'm sure there must be some way to do this that is already figured out and if someone knows of one let me know. I am currently using a ball for each column and each row with if statements that hide/show the various balls. So I can have the ball go across on row one and when it comes to a column it stops is hidden and the ball in that column is shown and moves up and down. The same with the ball in column one. As it moves up and down when it comes to a row it is hidden and the ball in that row is shown and moves across. When it gets back to row one it is hidden and the ball in row one starts moving up and down again. My problem is having the balls show/hide on the interior intersections i.e. row two column two, row two column three etc.
Any suggestions. On how to accomplish this.
I am using accelerometer motion.gravity to move the ball images vertical and horizontally.
this is some code I use to move the balls
if (ball.hidden == NO) {
self.ball.center = CGPointMake(self.ball.center.x + n, 9);
}
//ROW ONE START
if (ball.center.x < 9) {
ball.center = CGPointMake(9, 9);
}
if (ball.center.x > 311) {
ball.center = CGPointMake(311, 9);
}
if(self.ball.center.x > 9){
self.balla.hidden = YES;
balla.center = CGPointMake(9, 9);
}
if (ball.center.x ==9) {
//balla
CGPoint a = CGPointMake(9, 9);
if (CGPointEqualToPoint(ball.center,a)) {
balla.hidden = NO;
self.balla.center = CGPointMake(9, (self.balla.center.y)- y);
if (balla.center.y > 9) {
ball.center = CGPointMake(9,9);
ball.hidden = YES;
balla.hidden = NO;
self.balla.center = CGPointMake(9 , (self.balla.center.y)- y);
}
if (balla.center.y < 9) {
balla.center = CGPointMake(9,9);
ball.hidden = NO;
}
if (balla.center.y > 559) {
balla.center = CGPointMake(9, 559);
}
}
this is for the first row and the first column (there are additional columns here)
I also do additional rows
I have gotten the last column to move into the seventh and eighth rows with this code.
CGPoint e = CGPointMake(311, 9);
if (CGPointEqualToPoint(ball.center,e)) {
ball5a.hidden = YES;
self.ball5a.center = CGPointMake(311, (self.ball5a.center.y)- y);
if(self.ball.center.x < 311){
self.ball5a.hidden = YES;
}
if (ball5a.center.y < 9) {
ball5a.center = CGPointMake(311,9);
ball.hidden = NO;
ball5a.hidden = YES;
}
if (ball5a.center.y > 9) {
ball.center = CGPointMake(311,9);
ball5a.hidden = NO;
ball.hidden = YES;
self.ball5a.center = CGPointMake(311, (self.ball5a.center.y)- y);
}
if (self.ball5a.center.y> 480 && self.ball5a.center.y< 482) {
ball7.hidden = NO;
balla.center = CGPointMake(9, 481);
ball7.center = CGPointMake(311 , 481);
ball.center = CGPointMake(9, 9);
}
if (ball5a.center.y > 559) {
ball5a.center = CGPointMake(311, 559);
ball8.center = CGPointMake(311, 559);
ball.center = CGPointMake(9, 9);
}
if (ball5a.center.y <559) {
ball8.center =CGPointMake(9, 559);
ball.center = CGPointMake(311, 9);
ball8.hidden = YES;
}
and I can get the last row to move onto the last column with this
//ROW EIGHT START
CGPoint l = CGPointMake(9, 559);
if (CGPointEqualToPoint(balla.center,l)) {
balla.hidden = YES;
balla.center = CGPointMake(9, 559);
self.ball8.center = CGPointMake(self.ball8.center.x + n, 559);
ball8.hidden = NO;
}
if (ball8.center.x > 9) {
self.ball8.center = CGPointMake(self.ball8.center.x + n, 559);
balla.hidden = YES;
balla.center = CGPointMake(9, 559);
}
if (ball8.center.x < 10) {
ball8.center = CGPointMake(9, 559);
ball8.hidden = YES;
balla.hidden = NO;
}
if (ball8.center.x > 311) {
ball8.center = CGPointMake(311, 559);
ball5a.center = CGPointMake(311, 559);
ball5a.hidden = YES;
ball.center = CGPointMake(311, 9);
ball8.hidden = NO;
}
if (ball8.center.x < 311) {
ball5a.center = CGPointMake(311, 9);
ball5a.hidden = YES;
}
}
However when I try to move row seven onto column 5 I can get it to transfer but the ball in column 5 always ends up at coordinate (311,9) instead of the coordinate (311,481) that I specify in multiple places.
//ROW SEVEN START
CGPoint k = CGPointMake(9, 481);
if (CGPointEqualToPoint(balla.center,k)) {
ball5a.center = CGPointMake(311, 481);
balla.hidden = YES;
balla.center = CGPointMake(9, 481);
self.ball7.center = CGPointMake(self.ball7.center.x + n, 481);
ball7.hidden = NO;
}
if (ball7.center.x > 9) {
self.ball7.center = CGPointMake(self.ball7.center.x + n, 481);
balla.hidden = YES;
balla.center = CGPointMake(9, 481);
}
if (ball7.center.x < 10) {
ball7.center = CGPointMake(9, 481);
ball5a.center = CGPointMake(311, 481);
ball7.hidden = YES;
balla.hidden = NO;
}
if (ball7.center.x > 310) {
ball.center = CGPointMake(311, 9);
ball5a.center = CGPointMake(311, 481);
ball7.center = CGPointMake(311, 481);
ball5a.hidden = YES;
balla.center = CGPointMake(9, 481);
ball7.hidden = NO;
}
if (ball7.center.x < 310) {
ball5a.center = CGPointMake(311, 481);
ball5a.hidden = YES;
}
any ideas why this in not working right?

You could use double arrays, or a matrix (the same thing here, just another name) where an entry could either be null or your ball.
When you draw you will traverse the array and look at each entry, if the entry is null; you draw nothing. If the entry is not-null i.e. it is something, namely your ball, then draw it in that position the entry denotes.
We can use the double array to describe a coordinate system, or a grid, with columns and rows.
Consider an entry (i,j) to be column i and row j in your grid. So if the ball is in entry (2,4) it means that the ball is currently in column 2 and row 4, if we want to move the ball to another column or row, we simply set the entry (2,4) to be null and the set the desired entry, for instance (2,5) to be our ball.
And another thing, we can simply let null = 0 and the ball = 1 in our arrays, since the ball can be kept as a global variable somehow.
int matrix[20][20];
The above line will create an matrix/double-array of type int with the size of 20 columns and 20 rows.
Note that it would be good practice to run through every entry and set them to some init value, 0 in our case, I will not show that here. Consider it as an exercise ;)
Now we can position the ball somewhere:
matrix[2][4] = 1;
Now our ball is in column 2 and row 4.
If we want to move it to column 10 and row 15:
matrix[2][4] = 0; // Remember to remove the ball from its last position
matrix[10][15] = 1;
When you want to draw the ball you can run through the matrix/array and look at each entry and only draw the ball if the entry contains the number 1.
Even after you posted your code I was still slightly confused of what your exact problem/question was, but I hope this makes sense and helps!

Related

SpriteKit Block positioning

I am trying to make a jump game in Xcode 5 with SpriteKit.
First I want to spawn Blocks in a random position, but reachable for the 'human' in the game that is jumping.
I got a 1. Block, that spawn at a set position. From this position, there should spawn a 2. Block, that can be + or - 80 higher or lower, and < 80 away. Now here is the problem, I don`t know how to code it in Xcode. My code so far :
SKTexture * BlockTexture1 = [SKTexture textureWithImageNamed:#"Block"];
BlockTexture1.filteringMode = SKTextureFilteringNearest;
SKTexture * BlockTexture2 = [SKTexture textureWithImageNamed:#"Block"];
BlockTexture2.filteringMode = SKTextureFilteringNearest;
SKSpriteNode * Block1 = [SKSpriteNode spriteNodeWithTexture:BlockTexture1];
[Block1 setScale:1];
Block1.position = CGPointMake( 100 , 150 );
Block1.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:Block1.size];
Block1.physicsBody.dynamic = NO;
Block1.physicsBody.usesPreciseCollisionDetection = YES;
Block1.zPosition = 3;
[self addChild:Block1];
SKSpriteNode * Block2 = [SKSpriteNode spriteNodeWithTexture:BlockTexture2];
[Block2 setScale:1];
Block2.position = CGPointMake( **Block1 + or - 80, Block 1 < 80** );
Block2.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:Block2.size];
Block2.physicsBody.dynamic = NO;
Block2.physicsBody.usesPreciseCollisionDetection = YES;
Block2.zPosition = 3;
[self addChild:Block2];
To get your started, something like this perhaps:
int block1x = 100;
int block1y = 150;
Block1.position = CGPointMake(block1x, block1y);
int block2x = 0;
int block2y = 0;
int r1 = arc4random() % 2; // produces random numbers between 0 and 1
if(r1 == 0)
block2x = block1x + 80;
if(r1 == 1)
block2x = block1x - 80;
int r2 = arc4random() % 2;
if(r2 == 0)
block2y = block1y + 80;
if(r2 == 1)
block2y = block1y - 80;
Block2.position = CGPointMake(block2x, block2y);

Sprites are disappear after some time

Here some code for Add sprite for Enemies.....
_robbers = [[CCArray alloc] initWithCapacity:kNumAstroids];
for (int i = 0; i < kNumAstroids; ++i) {
CCSprite *asteroid = [CCSprite spriteWithSpriteFrameName:#"robber.png"];
asteroid.visible = NO;
[_batchNode addChild:asteroid];
[_robbers addObject:asteroid];
}
And in Update method ........
double curTime = CACurrentMediaTime();
if (curTime > _nextRunemanSpawn) {
float randSecs = [self randomValueBetween:0.20 andValue:1.0];
_nextRunemanSpawn = randSecs + curTime;
float randY = [self randomValueBetween:80 andValue:80];
float randDuration = [self randomValueBetween:4.5 andValue:4.5];
float randDuration1 = [self randomValueBetween:1.0 andValue:1.0];
CCSprite *asteroid = [_robbers objectAtIndex:_nextRobber];
_nextRobber++;
if (_nextRobber >= _robbers.count) {
_nextRobber = 1;
}
[asteroid stopAllActions];
asteroid.position = ccp(winSize.width +asteroid.contentSize.width / 2 , randY);
asteroid.visible = YES;
[asteroid runAction:[CCSequence actions:[CCMoveBy actionWithDuration:randDuration position:ccp(-winSize.width-asteroid.contentSize.width, 0)],
[CCCallFuncN actionWithTarget:self selector:#selector(setInvisible:)],nil]];
All Sprites are Move from right to left in screen
when Sprite crosses the middle of the screen it automatically disappear
what is the reason for this problem ??
I agree with the previously mentioned statement from LearnCocos2D. It is also possible that you are adding multiple objects and reaching the end of the capacity in the line
_robbers = [[CCArray alloc] initWithCapacity:kNumAstroids];
If you try to spawn more objects than whatever number you have specified for kNumAsteroids it will remove the oldest object and use the new one in its place. I.e. if kNumAsteroids is 5, you have 5 on the screen and then add a sixth, 1 will become 6 and its position will be whatever you set it to.

iPhone:How can I shuffle buttons in view

I have many no. of buttons on my view and I want to shuffle the buttons(change in position) then how can I shuffle the button in my view.
use arc4random()
and change position of your buttons
In your .h file : UIButton *buttonsarray[10];
In your .m file :
// Make array of button using following way.I used this method to creates button and you can use yours.
- (void)viewDidLoad {
[super viewDidLoad];
float y = 5;
int x = 0;
int count = 0;
for (int i = 0 ; i < 10; i++)
{
count ++;
buttonsarray[i] = [UIButton buttonWithType:UIButtonTypeRoundedRect];
buttonsarray[i].frame = CGRectMake(x, y, 100, 100);
[buttonsarray[i] setTitle:[NSString stringWithFormat:#"%d",i+1] forState:UIControlStateNormal];
x = x + 105;
[self.view addSubview:b[i]];
if(count == 3)
{
count = 0;
x = 0;
y = y+ 105;
}
}
}
// This function will soufflé your buttons
- (IBAction)btnClicked:(id)sender
{
int n = 10;
int swaper;
for (int i = 0 ; i < 10 ; i++)
{
int r = arc4random()%n;
if(r != swaper){
swaper = r;
CGRect r1 = buttonsarray[i].frame;
buttonsarray[i].frame = buttonsarray[swaper].frame;
buttonsarray[swaper].frame = r1;
n--;
}
}
}
Hope,this will help you..
You can do this
Make an Array with a group of CGPoints you will need to store the points as strings.
In the layoutSubViews method set something like this:
-(void)layoutSubViews{
[self.subviews enumerateObjectsUsingBlock:^(id object, NSUInteger idx, BOOL *stop) {
CGPoint newPoint = CGPointFromString([positionsArray objectAtIndex:idx]);
object.frame = CGRectMake(newPoint.x,newPoint.y,object.frame.size.width,object.frame.size.height);
}];
}
Then you will need to shuffle the positions in the positions array, you can see an example method here :How to Shuffle an Array
Now every time you need to Shuffle the positions you can call -(void)setNeedsLayout on your view.
There are more options but that was the first I thought of.
Good Luck

How to implement powerUps and other game altering objects in objective-C or cocos2d

Ok, so I have these powerups that I want to slow/speed up the movement of the other objects in the game for a few seconds.
I have an array of objects that I have a variable called spawnInterval that gets faster and faster as the game progresses, making the ame get harder after a few mins.
But I can't really grasp how to make it so the character in the game will react differently to different objects as in when the fastPowerUp is hit by the character sprite, the spawn interval doesn't change.
And vice versa with the slowPowerUp.
the code I have at the moment is this in a move sequence method that gets called in an update method:
-
(void) updateObstacles:(ccTime)delta{
for (int i = 0; i < 20; i++) {
//int randomizer = CCRANDOM_0_1() * [obstacles count];
//NSLog(#"randomizer: %i",randomizer);
CCSprite* randomObject = [obstacles randomObject];
currentObject = [obstacles indexOfObject:randomObject];
if ([randomObject numberOfRunningActions] == 0) {
[self runObstacleMoveSequence:randomObject withTimer:delta];
break;
}
}
}
-(void) runObstacleMoveSequence:(CCSprite *)object withTimer:(ccTime)delta{
static int time;
//Slowly increase object speed
numObstaclesMoved++;
if (!slowPowerUp && !fastPowerUp) {
time += delta;
if (numObstaclesMoved % 17 == 0 && obstacleMoveDuration > 2.0f) {
obstacleMoveDuration -= 0.2f;
if (spawnInterval > 0.1f) {
[self unschedule:#selector(updateObstacles:)];
[self schedule:#selector(updateObstacles:) interval:spawnInterval];
spawnInterval-=0.1f;
NSLog(#"interval: %f",spawnInterval);
}
}
}else if (slowPowerUp && !fastPowerUp) {
if (numObstaclesMoved % 17 == 0 && obstacleMoveDuration > 2.0f) {
obstacleMoveDuration += 3.0f;
if (spawnInterval > 0.1f) {
[self unschedule:#selector(updateObstacles:)];
[self schedule:#selector(updateObstacles:) interval:spawnInterval];
spawnInterval-=0.1f;
NSLog(#"interval: %f",spawnInterval);
if (time >= (delta + 3)) {
slowPowerUp = NO;
obstacleMoveDuration -= 3.0f;
}
}
}
}else if (!slowPowerUp && fastPowerUp) {
if (numObstaclesMoved % 17 == 0 && obstacleMoveDuration > 2.0f) {
obstacleMoveDuration -= 3.0f;
if (spawnInterval > 0.1f) {
[self unschedule:#selector(updateObstacles:)];
[self schedule:#selector(updateObstacles:) interval:spawnInterval];
spawnInterval-=0.1f;
NSLog(#"interval: %f",spawnInterval);
if (time >= (delta + 3)) {
fastPowerUp = NO;
obstacleMoveDuration += 3.0f;
}
}
}
}
CGSize screenSize = [[CCDirector sharedDirector]winSize];
CGPoint aboveScreenPosition = CGPointMake(object.position.x, screenSize.height - object.position.y);
int rotations = (CCRANDOM_0_1()*3) * 360;
float duration = (CCRANDOM_0_1()*5.0f) + 8.0f;
CCMoveTo* move = [CCMoveTo actionWithDuration:obstacleMoveDuration position:aboveScreenPosition];
CCRotateTo* rotate = [CCRotateBy actionWithDuration:duration angle:rotations];
CCSpawn* moveRotate = [CCSpawn actions: move, rotate, nil];
CCCallFuncN* call = [CCCallFuncN actionWithTarget:self selector:#selector(objectAboveScreen:)];
CCSequence* sequence = [CCSequence actions:moveRotate, call, nil];
[object runAction:sequence];
if (time >= (delta + 3)) {
fastPowerUp = NO;
}
}
-(void) objectAboveScreen:(id) sender{
//make sure sender is actually of the right class
NSAssert([sender isKindOfClass:[CCSprite class]], #"sender is not a CCSprite!");
CCSprite* obstacle = (CCSprite*)sender;
//move the back to the bottom of the screen
CGPoint pos = obstacle.position;
CGSize screenSize = [[CCDirector sharedDirector]winSize];
pos.y = (-screenSize.height - [obstacle texture].contentSize.height);
pos.x = CCRANDOM_0_1() * screenSize.width;
obstacle.position = pos;
}
I really just don't know where to go from here... Should I make the powerUps a different class? If so, how would I implement something like this? I really hate trying to ask for someone to solve my question, but I really just can't rack my brain around this and I'm rather new... if it were explained to me, then I know I would be able to implement it in future games on my own...
Thanks in advance, and let me know if more information is needed...
I'd do something like
in the .h file
float speedModifier;
-(void)resetPowerUp;
in the .m
-(void)resetPowerUp
{
speedModifier = 1;
}
wherever you are initializing the level
[self resetPowerUp];
upon collision with powerup:
speedModifier = 2;
[self performSelector:#selector(resetPowerUp) withObject:nil afterDelay:5];
then wherever you are moving whatever it is which speed should be effected by the powerup mode, multiply the speed of the animation (or divide the duration it takes for it to get wherever it's going) by speedModified
hope that helps

iPhone Map Kit cluster pinpoints

Regarding iPhone Map Kit cluster pinpoints:
I have 1000's of marks that I want to show on the map but it's just too many to handle so I want to cluster them.
Are there frameworks available or proof of concepts? That this is possible or is already been done?
You can use REVClusterMap to cluster
Note: This is a commercial product I'm affiliated with, but it solves this very problem.
I solved this problem in few of my apps and decided to extract it into a reusable framework. It's called Superpin and it is an (commercial, license costs $149) iOS Framework that internally uses quadtrees for annotation storage and performs grid-based clustering. The algorithm is quite fast, the included sample app is showing airports of the world (more than 30k+ annotations) and it's running quite smooth on an 3G iPhone.
This might be a bit like using a chainsaw to mow the lawn, but here is an excerpt from Algorithms in a Nutshell
Creating a KD-Tree...
public class KDFactory {
// Known comparators for partitioning points along dimensional axes.
private static Comparator<IMultiPoint> comparators[ ] ;
// Recursively construct KDTree using median method on input points.
public static KDTree generate (IMultiPoint [ ] points) {
if (points. length == 0) { return null; }
// median will be the root.
int maxD = points[ 0] . dimensionality( );
KDTree tree = new KDTree(maxD) ;
// Make dimensional comparators that compare points by ith dimension
comparators = new Comparator[ maxD+1] ;
for (int i = 1; i <= maxD; i++) {
comparators[ i] = new DimensionalComparator(i) ;
}
tree. setRoot(generate (1, maxD, points, 0, points. length-1) ) ;
return tree;
}
// generate the node for the d-th dimension (1 <= d <= maxD)
// for points[ left, right]
private static DimensionalNode generate (int d, int maxD,
IMultiPoint points[ ] ,
int left, int right) {
// Handle the easy cases first
if (right < left) { return null; }
if (right == left) { return new DimensionalNode (d, points[ left] ) ; }
// Order the array[ left, right] so the mth element will be the median
// and the elements prior to it will all be <=, though they won' t
// necessarily be sorted; similarly, the elements after will all be >=
int m = 1+(right-left) /2;
Selection. select(points, m, left, right, comparators[ d] ) ;
// Median point on this dimension becomes the parent
DimensionalNode dm = new DimensionalNode (d, points[ left+m-1] ) ;
// update to the next dimension, or reset back to 1
if (++d > maxD) { d = 1; }
// recursively compute left and right sub-trees, which translate
// into ' below' and ' above' for n-dimensions.
dm. setBelow(maxD, generate (d, maxD, points, left, left+m-2) ) ;
dm. setAbove(maxD, generate (d, maxD, points, left+m, right) ) ;
return dm;
}
}
Finding nearest neighbors best: O(log n) worst O(n)
// method in KDTree
public IMultiPoint nearest (IMultiPoint target) {
if (root == null) return null;
// find parent node to which target would have been inserted. This is our
// best shot at locating closest point; compute best distance guess so far
DimensionalNode parent = parent(target) ;
IMultiPoint result = parent. point;
double smallest = target. distance(result) ;
// now start back at the root, and check all rectangles that potentially
// overlap this smallest distance. If better one is found, return it.
double best[ ] = new double[ ] { smallest };
double raw[ ] = target. raw( );
IMultiPoint betterOne = root. nearest (raw, best) ;
if (betterOne ! = null) { return betterOne; }
return result;
}
// method in DimensionalNode. min[ 0] contains best computed shortest distance.
IMultiPoint nearest (double[ ] rawTarget, double min[ ] ) {
// Update minimum if we are closer.
IMultiPoint result = null;
// If shorter, update minimum
double d = shorter(rawTarget, min[ 0] ) ;
if (d >= 0 && d < min[ 0] ) {
min[ 0] = d;
result = point;
}
// determine if we must dive into the subtrees by computing direct
// perpendicular distance to the axis along which node separates
// the plane. If d is smaller than the current smallest distance,
// we could "bleed" over the plane so we must check both.
double dp = Math. abs(coord - rawTarget[ dimension-1] ) ;
IMultiPoint newResult = null;
if (dp < min[ 0] ) {
// must dive into both. Return closest one.
if (above ! = null) {
newResult = above. nearest (rawTarget, min) ;
if (newResult ! = null) { result = newResult; }
}
if (below ! = null) {
newResult = below. nearest(rawTarget, min) ;
if (newResult ! = null) { result = newResult; }
}
} else {
// only need to go in one! Determine which one now.
if (rawTarget[ dimension-1] < coord) {
if (below ! = null) {
newResult = below. nearest (rawTarget, min) ;
}
} else {
if (above ! = null) {
newResult = above. nearest (rawTarget, min) ;
}
}
// Use smaller result, if found.
if (newResult ! = null) { return newResult; }
}
return result;
}
More on KD-Trees at Wikipedia
I tried the others suggested here, and I also found OCMapView which has worked the best.
Its free and allows for easy grouping of annotations, which is what I needed. Its a bit newer & more updated than Revolver and to me is easier to implement.
A proof of concept is the Offline Maps app "OffMaps" ;)
http://itunes.apple.com/us/app/offmaps/id313854422?mt=8
I recently had to implement annotation clustering with MapKit. The solutions mentioned above are good, depending on your use case. I ended up going with FBAnnotationClustering (Objective-C) because it was free, and had lots of stars and few issues on github:
https://github.com/infinum/FBAnnotationClustering
The app I was working on was very map-centric, so it made sense to translate FBAnnotationClustering into Swift. Here's a blog post on the approach, which includes a link to the sample project on github.
http://ribl.co/blog/2015/05/28/map-clustering-with-swift-how-we-implemented-it-into-the-ribl-ios-app/
Inspired by WWDC 2011 video, this code works very well for me. Maybe not the fastest of all proposed here but it's for free and it's definitely the simplest.
It basically use 2 maps. One is hidden and hold every single annotation (allAnnotationMapView in my code). One is visible and show only the clusters or the annotations if single (mapView in my code).
- (void)didZoom:(UIGestureRecognizer*)gestureRecognizer {
if (gestureRecognizer.state == UIGestureRecognizerStateEnded){
[self updateVisibleAnnotations];
}
}
- (void)updateVisibleAnnotations {
static float marginFactor = 2.0f;
static float bucketSize = 50.0f;
MKMapRect visibleMapRect = [self.mapView visibleMapRect];
MKMapRect adjustedVisibleMapRect = MKMapRectInset(visibleMapRect, -marginFactor * visibleMapRect.size.width, -marginFactor * visibleMapRect.size.height);
CLLocationCoordinate2D leftCoordinate = [self.mapView convertPoint:CGPointZero toCoordinateFromView:self.view];
CLLocationCoordinate2D rightCoordinate = [self.mapView convertPoint:CGPointMake(bucketSize, 0) toCoordinateFromView:self.view];
double gridSize = MKMapPointForCoordinate(rightCoordinate).x - MKMapPointForCoordinate(leftCoordinate).x;
MKMapRect gridMapRect = MKMapRectMake(0, 0, gridSize, gridSize);
double startX = floor(MKMapRectGetMinX(adjustedVisibleMapRect) / gridSize) * gridSize;
double startY = floor(MKMapRectGetMinY(adjustedVisibleMapRect) / gridSize) * gridSize;
double endX = floor(MKMapRectGetMaxX(adjustedVisibleMapRect) / gridSize) * gridSize;
double endY = floor(MKMapRectGetMaxY(adjustedVisibleMapRect) / gridSize) * gridSize;
gridMapRect.origin.y = startY;
while(MKMapRectGetMinY(gridMapRect) <= endY) {
gridMapRect.origin.x = startX;
while (MKMapRectGetMinX(gridMapRect) <= endX) {
NSSet *allAnnotationsInBucket = [self.allAnnotationMapView annotationsInMapRect:gridMapRect];
NSSet *visibleAnnotationsInBucket = [self.mapView annotationsInMapRect:gridMapRect];
NSMutableSet *filteredAnnotationsInBucket = [[allAnnotationsInBucket objectsPassingTest:^BOOL(id obj, BOOL *stop) {
BOOL isPointMapItem = [obj isKindOfClass:[PointMapItem class]];
BOOL shouldBeMerged = NO;
if (isPointMapItem) {
PointMapItem *pointItem = (PointMapItem *)obj;
shouldBeMerged = pointItem.shouldBeMerged;
}
return shouldBeMerged;
}] mutableCopy];
NSSet *notMergedAnnotationsInBucket = [allAnnotationsInBucket objectsPassingTest:^BOOL(id obj, BOOL *stop) {
BOOL isPointMapItem = [obj isKindOfClass:[PointMapItem class]];
BOOL shouldBeMerged = NO;
if (isPointMapItem) {
PointMapItem *pointItem = (PointMapItem *)obj;
shouldBeMerged = pointItem.shouldBeMerged;
}
return isPointMapItem && !shouldBeMerged;
}];
for (PointMapItem *item in notMergedAnnotationsInBucket) {
[self.mapView addAnnotation:item];
}
if(filteredAnnotationsInBucket.count > 0) {
PointMapItem *annotationForGrid = (PointMapItem *)[self annotationInGrid:gridMapRect usingAnnotations:filteredAnnotationsInBucket];
[filteredAnnotationsInBucket removeObject:annotationForGrid];
annotationForGrid.containedAnnotations = [filteredAnnotationsInBucket allObjects];
[self.mapView addAnnotation:annotationForGrid];
//force reload of the image because it's not done if annotationForGrid is already present in the bucket!!
MKAnnotationView* annotationView = [self.mapView viewForAnnotation:annotationForGrid];
NSString *imageName = [AnnotationsViewUtils imageNameForItem:annotationForGrid selected:NO];
UILabel *countLabel = [[UILabel alloc] initWithFrame:CGRectMake(15, 2, 8, 8)];
[countLabel setFont:[UIFont fontWithName:POINT_FONT_NAME size:10]];
[countLabel setTextColor:[UIColor whiteColor]];
[annotationView addSubview:countLabel];
imageName = [AnnotationsViewUtils imageNameForItem:annotationForGrid selected:NO];
annotationView.image = [UIImage imageNamed:imageName];
if (filteredAnnotationsInBucket.count > 0){
[self.mapView deselectAnnotation:annotationForGrid animated:NO];
}
for (PointMapItem *annotation in filteredAnnotationsInBucket) {
[self.mapView deselectAnnotation:annotation animated:NO];
annotation.clusterAnnotation = annotationForGrid;
annotation.containedAnnotations = nil;
if ([visibleAnnotationsInBucket containsObject:annotation]) {
CLLocationCoordinate2D actualCoordinate = annotation.coordinate;
[UIView animateWithDuration:0.3 animations:^{
annotation.coordinate = annotation.clusterAnnotation.coordinate;
} completion:^(BOOL finished) {
annotation.coordinate = actualCoordinate;
[self.mapView removeAnnotation:annotation];
}];
}
}
}
gridMapRect.origin.x += gridSize;
}
gridMapRect.origin.y += gridSize;
}
}
- (id<MKAnnotation>)annotationInGrid:(MKMapRect)gridMapRect usingAnnotations:(NSSet *)annotations {
NSSet *visibleAnnotationsInBucket = [self.mapView annotationsInMapRect:gridMapRect];
NSSet *annotationsForGridSet = [annotations objectsPassingTest:^BOOL(id obj, BOOL *stop) {
BOOL returnValue = ([visibleAnnotationsInBucket containsObject:obj]);
if (returnValue) {
*stop = YES;
}
return returnValue;
}];
if (annotationsForGridSet.count != 0) {
return [annotationsForGridSet anyObject];
}
MKMapPoint centerMapPoint = MKMapPointMake(MKMapRectGetMinX(gridMapRect), MKMapRectGetMidY(gridMapRect));
NSArray *sortedAnnotations = [[annotations allObjects] sortedArrayUsingComparator:^(id obj1, id obj2) {
MKMapPoint mapPoint1 = MKMapPointForCoordinate(((id<MKAnnotation>)obj1).coordinate);
MKMapPoint mapPoint2 = MKMapPointForCoordinate(((id<MKAnnotation>)obj2).coordinate);
CLLocationDistance distance1 = MKMetersBetweenMapPoints(mapPoint1, centerMapPoint);
CLLocationDistance distance2 = MKMetersBetweenMapPoints(mapPoint2, centerMapPoint);
if (distance1 < distance2) {
return NSOrderedAscending;
}
else if (distance1 > distance2) {
return NSOrderedDescending;
}
return NSOrderedSame;
}];
return [sortedAnnotations objectAtIndex:0];
}
I think Foto Brisko (iTunes link) does this.
I do not think there is a Cocoa Touch framework for it.