This question already has answers here:
Closed 10 years ago.
I have a problem in my application i have a moving image it works fine.
But my image is also moving over a button, that i can't click when the image is before the button. How can i make sure that the image is moving on the background of my view so i can still press the button.
This is the code for the moving image
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[[self navigationController] setNavigationBarHidden:YES animated:YES];
[self loadImage];
image = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"cow.png"]];
image.frame =self.view.bounds;
[[self view] addSubview:image];
[NSTimer scheduledTimerWithTimeInterval: 3
target: self
selector:#selector(moveImage:)
userInfo: nil repeats:YES];
}
-(void) moveImage: (NSTimer*)timer {
CGFloat x = (CGFloat) (arc4random() % (int) self.view.bounds.size.width);
CGFloat y = (CGFloat) (arc4random() % (int) self.view.bounds.size.height);
CGPoint pointOne=CGPointMake(x,y);
image.center=pointOne;
}
}
The problem lies in the fact that arc4random_uniform() calls take an upper bounds, not a 0-based count of args (as you, cleverly some would say, extrapolated). Your equation is nearly-sound, however flawed in a few places, which I've tried to correct with some documentation:
-(void)someAction:(id)sender {
NSInteger imageIndex1 = arc4random_uniform(self.images.count); //upper bounds, so no -1
NSInteger imageIndex2 = arc4random_uniform(self.images.count); //upper bounds, so no -1
NSInteger imageIndex3 = arc4random_uniform(self.images.count); //upper bounds, so no -1
_Bool b1 = true, b2 = true, b3 = true; //I can only assume you've been declaring GNU C booleans because of the lowercase false.
//be careful, in Objective-C land, BOOL is signed char.
if (imageIndex1 == imageIndex2) {
b1 = false;
imageIndex2 = arc4random_uniform(self.images.count); //upper bounds, so no -1
}
if(imageIndex1 == imageIndex3) {
b2 = false;
imageIndex1 = arc4random_uniform(self.images.count); //upper bounds, so no -1
}
if (imageIndex2 == imageIndex3) {
b3 = false;
imageIndex3 = arc4random_uniform(self.images.count); //upper bounds, so no -1
}
//You have to use ors here, otherwise your UI will never actually update, considering that in checking
//for unique factors, then reassigning to another unique factor if a check fails, one of them has got
//to be true before the UI can update, rather than all 3 at once.
//Perhaps an individual check of each boolean would be more effective.
if(b1 == true || b2 == true || b3 == true ) {
[self.picture1 setImage:self.images[imageIndex1]];
[self.picture2 setImage:self.images[imageIndex2]];
[self.picture3 setImage:self.images[imageIndex3]];
}
}
I'm guessing you're looking for a random index that isn't the current index, so that you can pick a random, different index, rather than just a random index.
- (NSUInteger)randomUnsignedLessThan:(NSInteger)max excluding:(NSUInteger)exclude {
NSInteger firstTry = -1;
while (firstTry == exclude) firstTry = arc4random() % max;
return firstTry;
}
Note that this approach will always call arc4random once, and require 1+N calls with a probability of 1/max^N, so for low ranges, and high performance requirements, you might consider a different algorithm to exclude the one index.
Related
I want to set 5 buttons on uiview at random position. Buttons need maintain some spacing to each other. I mean buttons should not overlap to each other.
All buttons set on UIView come from corners with rotation animation.
btn1.transform = CGAffineTransformMakeRotation(40);
btn2.transform = CGAffineTransformMakeRotation(60);
btn3.transform = CGAffineTransformMakeRotation(90);
btn4.transform = CGAffineTransformMakeRotation(30);
btn5.transform = CGAffineTransformMakeRotation(20);
I Can rotate buttons using above code but can you pls. help me for set buttons on random position with out overlapping by each other.
If points are fix than I can set buttons with animation by this code but I want random position of buttons.
[AnimationView moveBubble:CGPointMake(18, 142) duration:1 : btn1];
[AnimationView moveBubble:CGPointMake(118, 142) duration:1 : btn2];
[AnimationView moveBubble:CGPointMake(193, 142) duration:1 : btn3];
[AnimationView moveBubble:CGPointMake(18, 216) duration:1 : btn4];
Thanks in advance.
1st, add buttons to an NSArray, only to make things easier:
NSArray *buttonArray = #[btn1,btn2,btn3,btn4,btn5];
Now, this code tries to Arrange them at random positions.
int xTemp, yTemp;
for (int i = 0; i < 5; i++) {
while (YES) {
xTemp = arc4random_uniform(view.frame.size.width - [buttonArray[i] frame].size.width);
yTemp = arc4random_uniform(view.frame.size.height - [buttonArray[i] frame].size.height);
if (![self positionx:xTemp y:yTemp intersectsAnyButtonTillIndex:i inButtonArray:buttonArray]) {
[AnimationView moveBubble:CGPointMake(xTemp, yTemp) duration:1 : buttonArray[i]];
break;
}
}
}
Implement this function somewhere too:
- (BOOL) positionx:(int)xTemp y:(int)yTemp intersectsAnyButtonTillIndex:(int)index inButtonArray:(NSArray *)buttonArray {
//Again please change the < to <= , I'm sorry, doing too many things at once.
for (int i = 0; i <= index; i++) {
CGRect frame = [buttonArray[i] frame];
//EDIT : In the logic earlier, I had wrongly done a minus where I should have done a plus.
if ((xTemp > frame.origin.x && xTemp < (frame.size.width + frame.origin.x)) && (yTemp > frame.origin.y && yTemp < (frame.size.height + frame.origin.y))) return YES;
}
return NO;
OK this is a workign soln., I hope, just added something to WolfLink's answer. Check This.
for (UIButton *button in buttonArray) {
button.frame = CGRectMake(arc4random_uniform(view.frame.size.width - button.frame.size.width), arc4random_uniform(view.frame.size.height - button.frame.size.height), button.frame.size.width, button.frame.size.height);
while ([self button:button intersectsButtonInArray:buttonArray]) {
button.frame = CGRectMake(arc4random_uniform(view.frame.size.width - button.frame.size.width), arc4random_uniform(view.frame.size.height - button.frame.size.height), button.frame.size.width, button.frame.size.height);
}
//another function
-(BOOL)button:(UIButton *)button intersectsButtonInArray:(NSArray *)array {
for (UIButton *testButton in array) {
if (CGRectIntersectsRect(button.frame, testButton.frame) && ![button isEqual:testButton]) {
return YES;
}
}
return NO;
}
Based on spiritofmysoul.wordpress's code:
//in the function where you randomize the buttons
NSArray *buttonArray = #[btn1,btn2,btn3,btn4,btn5];
for (UIButton *button in buttonArray) {
float widthOffset = self.frame.size.width-button.frame.size.width;
float heightOffset = self.frame.size.height-button.frame.size.height;
button.frame = CGRectMake(arc4random()%widthOffset, arc4random()%heightOffset, button.frame.size.width, button.frame.size.height);
while ([self button:button intersectsButtonInArray:buttonArray]) {
button.frame = CGRectMake(arc4random(), arc4random(), button.frame.size.width, button.frame.size.height);
}
//another function
-(BOOL)button:(UIButton *)button intersectsButtonInArray:(NSArray *)array {
for (UIButton *testButton in array) {
if (CGRectIntersectsRect(button.frame, testButton.frame) && ![button isEqual:testButton]) {
return YES;
}
}
return NO;
}
Beware: This will work well for small amounts of buttons on a large space but as you add buttons and you run out of space, this method will take much longer to run. If there is not enough space for all the buttons, it will become an infinite loop.
it is probably a very simple problem but I spent already a lot of time on it and just have given up...
I have a button and a textfield for speed calculations. I want the button label change once the button is pressed (km/h >> mph >> m/s >> km/h and so on) and speed recalculated. It sometimes works fine but very often it jumps to "else" statement even if the CurrentSpeedValue is #"km/h". Could anyone help? Maybe it would be better to use switch-case method but how should it be stated?
- (IBAction)speedChange:(id)sender {
//CurrentSpeedUnit is saved to NSUserDefault in another action
if (CurrentSpeedUnit == #"km/h") {
[sender setTitle:#"mph" forState:UIControlStateNormal];
CurrentSpeedUnit = #"mph";
float speedToPrint = ([textSpeed.text floatValue]) / 1.609344;
textSpeed.text = [[NSString alloc] initWithFormat:#"%.3f", speedToPrint];
} else if (CurrentSpeedUnit == #"mph") {
[sender setTitle:#"m/s" forState:UIControlStateNormal];
CurrentSpeedUnit = #"m/s";
float speedToPrint = ([textSpeed.text floatValue]) * 1.609344 / 3.6;
textSpeed.text = [[NSString alloc] initWithFormat:#"%.3f", speedToPrint];
} else {
[sender setTitle:#"km/h" forState:UIControlStateNormal];
CurrentSpeedUnit = #"km/h";
float speedToPrint = ([textSpeed.text floatValue]) * 3.6;
textSpeed.text = [[NSString alloc] initWithFormat:#"%.3f", speedToPrint];
}
}
For string comparison you need to use
isEqualToString
For Example:
if ([CurrentSpeedUnit isEqualToString:#"km/h"])
{
// Perfrom Action
}...
You should not compare strings like that (you compare pointers but not the contents). Use isEqualToString.
i.e.
if ([CurrentSpeedUnit isEqualToString:#"km/h"]) {
...
but not your
if (CurrentSpeedUnit == #"km/h") {
It may work sometimes, but just remember to avoid comparing strings with ==
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
Here is my code :
-(void)detectCollision{
imageView.center = CGPointMake(imageView.center.x + X, imageView.center.y + Y);
if(CGRectIntersectsRect(imageView.frame,centre.frame)){
label.text= [NSString stringWithFormat:#"%d", count];
++count;
}
I have a CADisplayLink(60fps) on detectCollision.
I want to increment "count" of one every time "imageView" collide with "centre", but my problem is that count increment too fast, every time there is a collision it increment of near 100 or 200, I don't know why. How can I solve this ?
Its because everytime the frames start intersecting in,
if(CGRectIntersectsRect(imageView.frame,centre.frame))
your condition will be true until the frames separate and the count increases over 100 in the CADisplayLink
so you can use a BOOL and set it to true when the frames first intersects. then check whether they are separate and make the BOOL back to false.
initialize BOOL intersectFlag to false in init. I assume that the frames wont intersect initially.
-(void)detectCollision
{
imageView.center = CGPointMake(imageView.center.x + X, imageView.center.y + Y);
if(!intersectFlag)
{
if(CGRectIntersectsRect(imageView.frame,centre.frame))
{
intersectFlag = YES;
label.text= [NSString stringWithFormat:#"%d", count];
++count;
}
}
else
{
if(!CGRectIntersectsRect(imageView.frame,centre.frame))
{
intersectFlag = NO;
}
}
}
to find out collision u should use with bounds not frame
bounds - size inside of object
frame - offset from superview x,y + size(bounds)
here i assume u are in portrait mode
-(void)detectCollision{
if(CGRectContainsPoint( imageView.bounds, CGPointMake(160, 240)) ){
label.text= [NSString stringWithFormat:#"%d", count];
++count;
}
}
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.