Is there a way to detect closest UIButton to tap location? - ios

Is there a way to easily detect the closest UIButton to a tap location? I've currently subclasses a pan gesture, but am just having trouble figuring out how to approach this. In the past I've resized every button's frame so that there isn't empty space in between buttons, but that won't be possible in my current case.
At the moment I've successfully looped through my subviews and can detect when I am / am not on top of a button. But how can I detect the closest button when I'm not on a button?
Thanks!
Here's my code for identifying my UIButtons:
- (UIView *)identifySubview:(NSSet *)touches {
CGPoint tapLocation = [[touches anyObject] locationInView:self.view];
for (UIView *view in [self.view viewWithTag:1000].subviews) {
for (UITableViewCell *cell in view.subviews) {
for (UIView *contentView in cell.subviews) {
for (UIButton *button in contentView.subviews) {
if ([button isKindOfClass:[UIButton class]]) {
CGRect trueBounds = [button convertRect:button.bounds toView:self.view];
if (CGRectContainsPoint(trueBounds, tapLocation)) {
return button;
} else {
//How would I detect the closest button?
}
}
}
}
}
}
return nil;
}

Failing a direct hit, you could find the minimum distance to the centers of the buttons like this:
// this answers the square of the distance, to save a sqrt operation
- (CGFloat)sqDistanceFrom:(CGPoint)p0 to:(CGPoint)p1 {
CGFloat dx = p0.x - p1.x;
CGFloat dy = p0.y - p1.y;
return dx*dx + dy*dy;
}
- (UIView *)nearestViewIn:(NSArray *)array to:(CGPoint)p {
CGFloat minDistance = FLT_MAX;
UIView *nearestView = nil;
for (UIView *view in array) {
CGFloat distance = [self sqDistanceFrom:view.center to:p];
if (distance < minDistance) {
minDistance = distance;
nearestView = view;
}
}
return nearestView;
}
// call ...
[self nearestViewIn:view.subviews to:tapLocation];

You may try to write in - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event

Related

Different coordinates while resizing an imageview iOS

I'm trying to resizing an imageview using these functions:
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch* mTouch = [touches anyObject];
if (mTouch.view == [self Logo_01]) {
CGPoint cp = [mTouch locationInView:[self view]];
[[mTouch view]setCenter:CGPointMake(cp.x-xd, cp.y-yd)];
NSLog(#"questo è x");
NSLog(#"lalal %f", cp.y);
if (cp.y > 390) {
[_Logo_01 setHidden:YES];
}
if (cp.y < 130) {
[_Logo_01 setHidden:YES];
}
if (cp.x > 290) {
[_Logo_01 setHidden:YES];
}
if (cp.x < 40) {
[_Logo_01 setHidden:YES];
}
}
when i resize my Logo on the app the image resizes correctly but my center point is wrong
I'm sorry i wronged to post code
this is the code that i use to scale my images:
- (IBAction)ScaleImage1:(UIPinchGestureRecognizer *)recognizer
{
recognizer.view.transform = CGAffineTransformScale(recognizer.view.transform, recognizer.scale, recognizer.scale);
recognizer.scale = 1;
CGFloat height = _Logo_01.frame.size.height;
NSLog(#"SIZE ===== %f", height);
if (height > 600) {
[_Logo_01 setHidden:YES];
}
}
try to write same style everywhere (brackets written randomly, spaces, newlines) - it's easier to read and shows that the dev is better.
Logo_01 is just horrible name for UI element. It's good for asset name. Refactor and rename it to eg startingLogoImageView.
Comparing views with == and isEqual works in a different way. One compares pointers, other one objects. Please read about it, as that's a serious bug if that's unintended.
What is xd in setCenter: line? Where is it from?
We try not to use shortcuts in Objective-C. You should name cp touchPoint as it's more obvious what it is in fact. That's the way recommended by Apple and used by devs.
So the code:
// extern NSUInteger const kViewOffsetY; // uncomment this line if you want it accessible via other classes, delete otherwise
NSUInteger const kViewOffsetY = 1;
NSUInteger const kViewOffsetX = 1;
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
if ([touch.view isEqual:startingLogoImageView]) {
UIView *touchedView = touch.view;
CGPoint touchPoint = [touch locationInView:self.view];
[touchedView setCenter:CGPointMake(CGRectGetMidX(touchedView.frame) - kViewOffsetX, CGRectGetMidY(touchedView.frame) - kViewOffsetY)];
// where are these values from? you probably should calculate it more dynamically
BOOL shouldHideView = touchPoint.y > 390 || touchPoint.y < 130 || touchPoint.x > 290 || touchPoint.x < 40;
if (shouldHideView) {
[touchedView setHidden:YES];
}
}
}
Why was it working wrong? Because in setCenter: method you were using top left corner (x and y values of view are in top left corner not in the center), while you should use the center point.

How to Detect Some Portion in View

I have made a Custom Circle View, How to Detect Some Portion or Part of Circle.I have Tried this using touches
- (BOOL)validatePoint:(CGPoint)myPoint
{
// calculate how far from centre we are with Pythagorean
// √ a2 + b2
CGFloat a = abs(myPoint.x - (self.bounds.size.width/2));
CGFloat b = abs(myPoint.y - (self.bounds.size.height/2));
CGFloat distanceFromCentre = sqrt(pow(a,2) + pow(b,2));
if((distanceFromCentre > self.minRadiusSize) && (distanceFromCentre < radius)){
return YES;
}else{
// not inside doughnut
return NO;
}
}
But its Detecting Entire my View.
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
BOOL transparent=NO;
CGPoint touchLocation = [event locationInView:yourViewName];
if(CGRectContainsPoint(yourViewName.frame, touchLocation))
{
NSLog(#"Found =%d",i);
transparent=YES;
}
return transparent;
}
This will return you whether you have tapped in view or not.

Drag UIButton from one view to another by using touch method

Hello all,
I am using "Touch methods" first time so facing certain issue.
In my app, there are 2 subviews in mainview.I am trying to implement drag and drop of uibutton from one view to another view.
I'm trying lots of things but not able to do drag and drop.
please help me.
Thanks in advance.
My Code :
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
CGPoint touchPoint = [[touches anyObject] locationInView:self.viewblue];
for (UIButton *iView in self.view.subviews) {
if ([iView isMemberOfClass:[UIButton class]])
{
if (touchPoint.x > iView.frame.origin.x &&
touchPoint.x < iView.frame.origin.x + iView.frame.size.width &&
touchPoint.y > iView.frame.origin.y &&
touchPoint.y < iView.frame.origin.y + iView.frame.size.height)
{
self.btnalpha = iView;
touchOffset = CGPointMake(touchPoint.x - iView.frame.origin.x,
touchPoint.y - iView.frame.origin.y);
homePosition = CGPointMake(iView.frame.origin.x,
iView.frame.origin.y);
[self.viewblue bringSubviewToFront:self.btnalpha];
}
}
}
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
CGPoint touchPoint = [[touches anyObject] locationInView:self.viewblue];
CGRect newDragObjectFrame = CGRectMake(touchPoint.x - touchOffset.x,
touchPoint.y - touchOffset.y,
self.btnalpha.frame.size.width,
self.btnalpha.frame.size.height);
self.btnalpha.frame = newDragObjectFrame;
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
CGPoint touchPoint = [[touches anyObject] locationInView:self.viewblue];
if (touchPoint.x > self.vieworange.frame.origin.x &&
touchPoint.x < self.vieworange.frame.origin.x + self.vieworange.frame.size.width &&
touchPoint.y > self.vieworange.frame.origin.y &&
touchPoint.y < self.vieworange.frame.origin.y + self.vieworange.frame.size.height )
self.btnalpha.frame = CGRectMake(self.homePosition.x, self.homePosition.y, self.btnalpha.frame.size.width, self.btnalpha.frame.size.height);
}
- (void) drag
{
str_scramble =#"IH";
for (int i = 0; i < 2; i++)
{
self.btnalpha = [[UIButton alloc] init];
[self.btnalpha setFrame:CGRectMake(i * 50 + 10, 5, 42, 42)];
char letter = [str_scramble characterAtIndex:i];
self.btnalpha.backgroundColor = [UIColor grayColor];
self.btnalpha.titleLabel.font = [UIFont boldSystemFontOfSize:20];
[self.btnalpha setTitle:[NSString stringWithFormat:#"%c", letter] forState:UIControlStateNormal];
[self.btnalpha addTarget:self action:#selector(touchesBegan:withEvent:) forControlEvents:UIControlEventTouchDown];
[self.btnalpha addTarget:self action:#selector(touchesMoved:withEvent:) forControlEvents:UIControlEventTouchDragInside];
[viewblue addSubview:self.btnalpha];
NSLog(#"string : --> %#", self.btnalpha.titleLabel.text);
}
}
you should use touchDrag methodes like touchDragEnter, touchDragExit, touchDragInside or touchDragOutside to drag a UIButton.
These all events are UIControlEvents
I am using PanGestureRecognizer and it works great.
UIPanGestureRecognizer *drag = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(dragButton:)];
drag.delegate = self;
drag.view.tag = position;
[button addGestureRecognizer:drag];
- (void)dragButton:(UIPanGestureRecognizer *)gesture
{
buttonPicked = [allObjects objectAtIndex:gesture.view.tag -1];
if (gesture.state == UIGestureRecognizerStateBegan)
{
originalPoint = buttonPicked.center;
}
else
{
buttonPicked.center = [gesture locationInView:self.view];
}
}
If you want some kind of special treatment for the end gesture (such as only to drop if in a certain location) - you can use the UIGestureRecognizedStateEnd.
if it's still not working - in the UIGestureRecognizerStateBegan, try to remove the button from the subview and add it to the main view, and in the UIGestureRecognizerStateEnd check which subview the button is in and add it instead of the main view. Should do the trick.
Good luck.
According to apple Documentation on Event Handling Guide,
when you first touch on button, it becomes first responder.SO, touchesBegan:withEvent in your ViewController is not called.
Disable the userInteraction of UIbutton and now viewcontroller will be first responder and starts receiving touch events.
button.userInteractionEnabled=NO;
Eventhough you addGestures to ViewController Or any of those Views, they wont respond when your touches are on button.

iOS + MKMapView user touch based drawing

I have searched a lot for this question, but none of them seem to do exactly what I want.
A lot of tutorials show me how to add lines and polygons in code, but not with freehand drawing.
The question is the following one:
I am building a real estate application. If the user is on the MKMapView it has the ability to draw a rectangle/circle/... around a certain area where he/she wants to buy/rent a house. Then I need to display the results that correspond within the area the user has selected.
Currently I have a UIView on top of my MKMapView where I do some custom drawing, is there a way to translate points to coordinates from that or ..? Or is this completely not the way this is done ? I have also heard about MKMapOverlayView, etc .. but am not exactly sure how to use this.
Can anybody point me in the right direction or does he have some sample code or a tutorial that can help me accomplish what I am in need for?
Thanks
I have an app that basically does this. I have a map view, with a toolbar at the top of the screen. When you press a button on that toolbar, you are now in a mode where you can swipe your finger across the map. The start and end of the swipe will represent the corners of a rectangle. The app will draw a translucent blue rectangle overlay to show the area you've selected. When you lift your finger, the rectangular selection is complete, and the app begins a search for locations in my database.
I do not handle circles, but I think you could do something similar, where you have two selection modes (rectangular, or circular). In the circular selection mode, the swipe start and end points could represent circle center, and edge (radius). Or, the two ends of a diameter line. I'll leave that part to you.
Implementation
First, I define a transparent overlay layer, that handles selection (OverlaySelectionView.h):
#import <QuartzCore/QuartzCore.h>
#import <MapKit/MapKit.h>
#protocol OverlaySelectionViewDelegate
// callback when user finishes selecting map region
- (void) areaSelected: (CGRect)screenArea;
#end
#interface OverlaySelectionView : UIView {
#private
UIView* dragArea;
CGRect dragAreaBounds;
id<OverlaySelectionViewDelegate> delegate;
}
#property (nonatomic, assign) id<OverlaySelectionViewDelegate> delegate;
#end
and OverlaySelectionView.m:
#import "OverlaySelectionView.h"
#interface OverlaySelectionView()
#property (nonatomic, retain) UIView* dragArea;
#end
#implementation OverlaySelectionView
#synthesize dragArea;
#synthesize delegate;
- (void) initialize {
dragAreaBounds = CGRectMake(0, 0, 0, 0);
self.userInteractionEnabled = YES;
self.multipleTouchEnabled = NO;
self.backgroundColor = [UIColor clearColor];
self.opaque = NO;
self.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
}
- (id) initWithCoder: (NSCoder*) coder {
self = [super initWithCoder: coder];
if (self != nil) {
[self initialize];
}
return self;
}
- (id) initWithFrame: (CGRect) frame {
self = [super initWithFrame: frame];
if (self != nil) {
[self initialize];
}
return self;
}
- (void)drawRect:(CGRect)rect {
// do nothing
}
#pragma mark - Touch handling
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch* touch = [[event allTouches] anyObject];
dragAreaBounds.origin = [touch locationInView:self];
}
- (void)handleTouch:(UIEvent *)event {
UITouch* touch = [[event allTouches] anyObject];
CGPoint location = [touch locationInView:self];
dragAreaBounds.size.height = location.y - dragAreaBounds.origin.y;
dragAreaBounds.size.width = location.x - dragAreaBounds.origin.x;
if (self.dragArea == nil) {
UIView* area = [[UIView alloc] initWithFrame: dragAreaBounds];
area.backgroundColor = [UIColor blueColor];
area.opaque = NO;
area.alpha = 0.3f;
area.userInteractionEnabled = NO;
self.dragArea = area;
[self addSubview: self.dragArea];
[dragArea release];
} else {
self.dragArea.frame = dragAreaBounds;
}
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
[self handleTouch: event];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
[self handleTouch: event];
if (self.delegate != nil) {
[delegate areaSelected: dragAreaBounds];
}
[self initialize];
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
[self initialize];
[self.dragArea removeFromSuperview];
self.dragArea = nil;
}
#pragma mark -
- (void) dealloc {
[dragArea release];
[super dealloc];
}
#end
Then I have a class that implements the protocol defined above (MapViewController.h):
#import "OverlaySelectionView.h"
typedef struct {
CLLocationDegrees minLatitude;
CLLocationDegrees maxLatitude;
CLLocationDegrees minLongitude;
CLLocationDegrees maxLongitude;
} LocationBounds;
#interface MapViewController : UIViewController<MKMapViewDelegate, OverlaySelectionViewDelegate> {
LocationBounds searchBounds;
UIBarButtonItem* areaButton;
And in my MapViewController.m, the areaSelected method is where I perform the conversion of touch coordinates to geographic coordinates with convertPoint:toCoordinateFromView: :
#pragma mark - OverlaySelectionViewDelegate
- (void) areaSelected: (CGRect)screenArea
{
self.areaButton.style = UIBarButtonItemStyleBordered;
self.areaButton.title = #"Area";
CGPoint point = screenArea.origin;
// we must account for upper nav bar height!
point.y -= 44;
CLLocationCoordinate2D upperLeft = [mapView convertPoint: point toCoordinateFromView: mapView];
point.x += screenArea.size.width;
CLLocationCoordinate2D upperRight = [mapView convertPoint: point toCoordinateFromView: mapView];
point.x -= screenArea.size.width;
point.y += screenArea.size.height;
CLLocationCoordinate2D lowerLeft = [mapView convertPoint: point toCoordinateFromView: mapView];
point.x += screenArea.size.width;
CLLocationCoordinate2D lowerRight = [mapView convertPoint: point toCoordinateFromView: mapView];
searchBounds.minLatitude = MIN(lowerLeft.latitude, lowerRight.latitude);
searchBounds.minLongitude = MIN(upperLeft.longitude, lowerLeft.longitude);
searchBounds.maxLatitude = MAX(upperLeft.latitude, upperRight.latitude);
searchBounds.maxLongitude = MAX(upperRight.longitude, lowerRight.longitude);
// TODO: comment out to keep search rectangle on screen
[[self.view.subviews lastObject] removeFromSuperview];
[self performSelectorInBackground: #selector(lookupHistoryByArea) withObject: nil];
}
// this action is triggered when user selects the Area button to start selecting area
// TODO: connect this to areaButton yourself (I did it in Interface Builder)
- (IBAction) selectArea: (id) sender
{
PoliteAlertView* message = [[PoliteAlertView alloc] initWithTitle: #"Information"
message: #"Select an area to search by dragging your finger across the map"
delegate: self
keyName: #"swipe_msg_read"
cancelButtonTitle: #"Ok"
otherButtonTitles: nil];
[message show];
[message release];
OverlaySelectionView* overlay = [[OverlaySelectionView alloc] initWithFrame: self.view.frame];
overlay.delegate = self;
[self.view addSubview: overlay];
[overlay release];
self.areaButton.style = UIBarButtonItemStyleDone;
self.areaButton.title = #"Swipe";
}
You'll notice that my MapViewController has a property, areaButton. That's a button on my toolbar, which normally says Area. After the user presses it, they are in area selection mode at which point, the button label changes to say Swipe to remind them to swipe (maybe not the best UI, but that's what I have).
Also notice that when the user presses Area to enter area selection mode, I show them an alert that tells them that they need to swipe. Since this is probably only a reminder they need to see once, I have used my own PoliteAlertView, which is a custom UIAlertView that users can suppress (don't show the alert again).
My lookupHistoryByArea is just a method that searches my database for locations, by the saved searchBounds (in the background), and then plots new overlays on the map at the found locations. This will obviously be different for your app.
Limitations
Since this is for letting the user select approximate areas, I did not consider geographic precision to be critical. It doesn't sound like it should be in your app, either. Thus, I just draw rectangles with 90 degree angles, not accounting for earth curvature, etc. For areas of just a few miles, this should be fine.
I had to make some assumptions about your phrase touch based drawing. I decided that both the easiest way to implement the app, and the easiest for a touchscreen user to use, was to simply define the area with one single swipe. Drawing a rectangle with touches would require 4 swipes instead of one, introduce the complexity of non-closed rectangles, yield sloppy shapes, and probably not get the user what they even wanted. So, I tried to keep the UI simple. If you really want the user drawing on the map, see this related answer which does that.
This app was written before ARC, and not changed for ARC.
In my app, I actually do use mutex locking for some variables accessed on the main (UI) thread, and in the background (search) thread. I took that code out for this example. Depending on how your database search works, and how you choose to run the search (GCD, etc.), you should make sure to audit your own thread-safety.
ViewController.h
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController
#end
ViewController.m
#import "ViewController.h"
#import <MapKit/MapKit.h>
#interface ViewController () <MKMapViewDelegate>
#property (weak, nonatomic) IBOutlet MKMapView *mapView;
#property (nonatomic, weak) MKPolyline *polyLine;
#property (nonatomic, strong) NSMutableArray *coordinates;
#property (weak, nonatomic) IBOutlet UIButton *drawPolygonButton;
#property (nonatomic) BOOL isDrawingPolygon;
#end
#implementation ViewController
#synthesize coordinates = _coordinates;
- (NSMutableArray*)coordinates
{
if(_coordinates == nil) _coordinates = [[NSMutableArray alloc] init];
return _coordinates;
}
- (void)viewDidLoad
{
[super viewDidLoad];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
- (IBAction)didTouchUpInsideDrawButton:(UIButton*)sender
{
if(self.isDrawingPolygon == NO) {
self.isDrawingPolygon = YES;
[self.drawPolygonButton setTitle:#"done" forState:UIControlStateNormal];
[self.coordinates removeAllObjects];
self.mapView.userInteractionEnabled = NO;
} else {
NSInteger numberOfPoints = [self.coordinates count];
if (numberOfPoints > 2)
{
CLLocationCoordinate2D points[numberOfPoints];
for (NSInteger i = 0; i < numberOfPoints; i++)
points[i] = [self.coordinates[i] MKCoordinateValue];
[self.mapView addOverlay:[MKPolygon polygonWithCoordinates:points count:numberOfPoints]];
}
if (self.polyLine)
[self.mapView removeOverlay:self.polyLine];
self.isDrawingPolygon = NO;
[self.drawPolygonButton setTitle:#"draw" forState:UIControlStateNormal];
self.mapView.userInteractionEnabled = YES;
}
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
if (self.isDrawingPolygon == NO)
return;
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInView:self.mapView];
CLLocationCoordinate2D coordinate = [self.mapView convertPoint:location toCoordinateFromView:self.mapView];
[self addCoordinate:coordinate];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
if (self.isDrawingPolygon == NO)
return;
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInView:self.mapView];
CLLocationCoordinate2D coordinate = [self.mapView convertPoint:location toCoordinateFromView:self.mapView];
[self addCoordinate:coordinate];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
if (self.isDrawingPolygon == NO)
return;
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInView:self.mapView];
CLLocationCoordinate2D coordinate = [self.mapView convertPoint:location toCoordinateFromView:self.mapView];
[self addCoordinate:coordinate];
[self didTouchUpInsideDrawButton:nil];
}
- (void)addCoordinate:(CLLocationCoordinate2D)coordinate
{
[self.coordinates addObject:[NSValue valueWithMKCoordinate:coordinate]];
NSInteger numberOfPoints = [self.coordinates count];
if (numberOfPoints > 2) {
MKPolyline *oldPolyLine = self.polyLine;
CLLocationCoordinate2D points[numberOfPoints];
for (NSInteger i = 0; i < numberOfPoints; i++) {
points[i] = [self.coordinates[i] MKCoordinateValue];
}
MKPolyline *newPolyLine = [MKPolyline polylineWithCoordinates:points count:numberOfPoints];
[self.mapView addOverlay:newPolyLine];
self.polyLine = newPolyLine;
if (oldPolyLine) {
[self.mapView removeOverlay:oldPolyLine];
}
}
}
#pragma mark - MKMapViewDelegate
- (MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id <MKOverlay>)overlay
{
MKOverlayPathView *overlayPathView;
if ([overlay isKindOfClass:[MKPolygon class]])
{
overlayPathView = [[MKPolygonView alloc] initWithPolygon:(MKPolygon*)overlay];
overlayPathView.fillColor = [[UIColor cyanColor] colorWithAlphaComponent:0.2];
overlayPathView.strokeColor = [[UIColor blueColor] colorWithAlphaComponent:0.7];
overlayPathView.lineWidth = 3;
return overlayPathView;
}
else if ([overlay isKindOfClass:[MKPolyline class]])
{
overlayPathView = [[MKPolylineView alloc] initWithPolyline:(MKPolyline *)overlay];
overlayPathView.strokeColor = [[UIColor blueColor] colorWithAlphaComponent:0.7];
overlayPathView.lineWidth = 3;
return overlayPathView;
}
return nil;
}
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation
{
if ([annotation isKindOfClass:[MKUserLocation class]])
return nil;
static NSString * const annotationIdentifier = #"CustomAnnotation";
MKAnnotationView *annotationView = [mapView dequeueReusableAnnotationViewWithIdentifier:annotationIdentifier];
if (annotationView)
{
annotationView.annotation = annotation;
}
else
{
annotationView = [[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:annotationIdentifier];
annotationView.image = [UIImage imageNamed:#"annotation.png"];
annotationView.alpha = 0.5;
}
annotationView.canShowCallout = NO;
return annotationView;
}
#end
or You can find here the entire project :
https://github.com/tazihosniomar/MapKitDrawing
i hope it will help you.
this is my way how I convert the touches to CLLocation on the MKMapView.
it works with the the Google Maps and the Apple Maps as well:
- (void)viewDidLoad {
// ...
// ... where the _customMapView is a MKMapView object;
// find the gesture recogniser of the map
UIGestureRecognizer *_factoryDoubleTapGesture = nil;
NSArray *_gestureRecognizersArray = [_customMapView gestureRecognizers];
for (UIGestureRecognizer *_tempRecogniser in _gestureRecognizersArray) {
if ([_tempRecogniser isKindOfClass:[UITapGestureRecognizer class]]) {
if ([(UITapGestureRecognizer *)_tempRecogniser numberOfTapsRequired] == 2) {
_factoryDoubleTapGesture = _tempRecogniser;
break;
}
}
}
// my tap gesture recogniser
UITapGestureRecognizer *_locationTapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(mapLocationTouchedUpInside:)];
if (_factoryDoubleTapGesture) [_locationTapGesture requireGestureRecognizerToFail:_factoryDoubleTapGesture];
[_customMapView addGestureRecognizer:_locationTapGesture];
// ...
}
and...
- (void)mapLocationTouchedUpInside:(UITapGestureRecognizer *)sender {
CGPoint _tapPoint = [sender locationInView:_customMapView];
CLLocationCoordinate2D _coordinates = [_customMapView convertPoint:_tapPoint toCoordinateFromView:_customMapView];
// ... do whatever you'd like with the coordinates
}
Try MKOverlayPathView. The problem in denoting a region by drawing a path on an MKMapView is, unless you know the zoom scale you don't know much. So you have to track that.

Why am not getting collision detection with CGRectIntersectsRect in IOS?

I have 4 elements: greenball, yellowball, orangeball, and redball that fall from the top of the screen
I also have an element, blueball, that follows my touch.
All of these things are working great! :D
However, I want to detect when the greenball intersects with the blueball
my blue ball is placed in the view at viewDidLoad, the 4 balls are placed on the view based on an nstimer that calls onTimer.
All 5 balls are created on the same layer:
[self.view insertSubview:greenImage belowSubview:bottomBar]
I can detect when the redball and greenball collide:
if (CGRectIntersectsRect(greenImage.frame, redImage.frame)) {
UIAlertView *message = [[UIAlertView alloc] initWithTitle:#"title" message:#"points" delegate:nil cancelButtonTitle:#"cool" otherButtonTitles:nil];
[message show];
[message release];
}
If I replace "redImage" for "blueball" I don't get the alert. I assume this may be because they aren't declared in the same method.
Any ideas on how to detect this collision?
thanks!
--- EDIT ---
Using the suggestion below I'm doing the following:
greenImage = [[UIImageView alloc] initWithImage:green];
greenImage.frame = CGRectMake(startX,0,43,43);
[self.view insertSubview:greenImage belowSubview:bottomBar];
[UIView beginAnimations:nil context:greenImage];
[UIView setAnimationDuration:5 * speed];
greenImage.frame = CGRectMake(startX, 500.0, 43,43);
CGFloat r1 = greenImage.frame.size.width * .5;
CGFloat r2 = blueBall.frame.size.width * .5;
if(CGPointDistance(greenImage.center, blueBall.center) < (r1 + r2)){
UIAlertView *message = [[UIAlertView alloc] initWithTitle:#"notice" message:#"hit!" delegate:nil cancelButtonTitle:#"cool" otherButtonTitles:nil];
[message show];
[message release];
}
below this method I have:
CGFloat CGPointDistance(CGPoint p1, CGPoint p2)
{
return sqrtf((p1.x-p2.x)*(p1.x-p2.x) + (p1.y-p2.y)*(p1.y-p2.y));
}
get an error:
conflicting types for 'CGPointDistance'
and a warning:
implicit declaration of function 'CGPointDistance'
To determine if two circles intersect, you simply see if the distance between the centers is less than the sum if their radii. For example...
CGFloat CGPointDistance(CGPoint p1, CGPoint p2)
{
return sqrtf((p1.x-p2.x)*(p1.x-p2.x) + (p1.y-p2.y)*(p1.y-p2.y));
}
It looks like your "balls" are views... Assuming the "ball" fills the frame, the radius will be 1/2 the width...
BOOL BallsCollide(UIView *v1, UIView *v2)
{
CGFloat r1 = v1.frame.size.width * .5;
CGFloat r2 = v2.frame.size.width * .5;
return CGPointDistance(v1.center, v2.center) < (r1 + r2);
}
OK -- I have hacked up a quick sample. You can almost drop it into your view controller. Don't use it for production stuff... just as an example of how you can do stuff... You can put this at the top and the rest of your controller below it...
// I do not recommend using this approach for naything non-trivial,
// but it demonstrates the very simple collision detection for circle shapes.
#interface Ball : UIView {
}
#property (nonatomic, strong) UIColor *origColor;
#property (nonatomic, strong) UIColor *color;
+ (id)ballWithRadius:(CGFloat)radius color:(UIColor*)color;
- (BOOL)doesIntersectWith:(Ball*)ball;
#end
#implementation Ball
#synthesize color = _color;
#synthesize origColor = _origColor;
+ (id)ballWithRadius:(CGFloat)radius color:(UIColor*)color
{
CGFloat diameter = radius * 2;
Ball *ball = [[Ball alloc] initWithFrame:CGRectMake(0, 0, diameter, diameter)];
ball.color = ball.origColor = color;
ball.backgroundColor = [UIColor clearColor];
return ball;
}
- (BOOL)doesIntersectWith:(Ball *)ball
{
// Two circles overlap if the sum of their radii
// is less than the distance between the two centers.
CGFloat r1 = self.frame.size.width * .5;
CGFloat r2 = ball.frame.size.width * .5;
CGFloat diffX = self.center.x - ball.center.x;
CGFloat diffY = self.center.y - ball.center.y;
return (diffX*diffX) + (diffY*diffY) < (r1+r2)*(r1+r2);
}
- (BOOL)containsPoint:(CGPoint)point
{
// Contains a point if distance from center is less than radius
CGFloat r = self.frame.size.width *.5;
CGFloat diffX = self.center.x - point.x;
CGFloat diffY = self.center.y - point.y;
return (diffX*diffX) + (diffY*diffY) < r*r;
}
- (void)drawRect:(CGRect)rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
[self.color setFill];
[self.color setStroke];
CGContextSetLineWidth(context, 1);
CGContextFillEllipseInRect(context, self.bounds);
}
#end
#interface CollideVC() {
NSMutableDictionary *activeTouches;
NSMutableSet *currentCollisions;
}
#end
#implementation CollideVC
- (void)ball:(Ball*)ball touched:(UITouch*)touch
{
[activeTouches setObject:ball forKey:[NSValue valueWithPointer:(__bridge void*)touch]];
}
- (Ball*)getBallTouchedBy:(UITouch*)touch
{
return [activeTouches objectForKey:[NSValue valueWithPointer:(__bridge void*)touch]];
}
- (void)stopTouch:(UITouch*)touch
{
[activeTouches removeObjectForKey:[NSValue valueWithPointer:(__bridge void*)touch]];
}
- (void)ball:(Ball*)ball1 hasCollidedWith:(Ball*)ball2
{
[currentCollisions addObject:ball1];
[currentCollisions addObject:ball2];
ball1.color = ball2.color = [UIColor yellowColor];
[ball1 setNeedsDisplay];
[ball2 setNeedsDisplay];
}
// NOTE: You should delegate handling of collisions...
- (void)checkForCollisions
{
// Should filter those that don't actually need redisplay...
// For simplicity, all that were or are colliding get it...
[currentCollisions enumerateObjectsUsingBlock:^(Ball *ball, BOOL *stop) {
[ball setNeedsDisplay];
ball.color = ball.origColor;
}];
[currentCollisions removeAllObjects];
NSArray *views = self.view.subviews;
for (size_t i = 0; i < views.count; ++i) {
id v = [views objectAtIndex:i];
if (![v isKindOfClass:[Ball class]]) continue;
Ball *ball1 = v;
for (size_t j = i+1; j < views.count; ++j) {
v = [views objectAtIndex:j];
if (![v isKindOfClass:[Ball class]]) continue;
Ball *ball2 = v;
if ([ball1 doesIntersectWith:ball2]) {
[self ball:ball1 hasCollidedWith:ball2];
}
}
}
}
- (void)addBallWithRadius:(CGFloat)radius point:(CGPoint)point color:(UIColor*)color
{
Ball *ball = [Ball ballWithRadius:radius color:color];
ball.center = point;
[self.view addSubview:ball];
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
for (UITouch *touch in touches) {
CGPoint touchLocation = [touch locationInView:self.view];
for (Ball *ball in self.view.subviews) {
if ([ball containsPoint:touchLocation]) {
[self ball:ball touched:touch];
}
}
}
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
for (UITouch *touch in touches) {
// Use relative distance so center does nt jump to tap location.
CGPoint prev = [touch previousLocationInView:self.view];
CGPoint curr = [touch locationInView:self.view];
CGPoint c = [self getBallTouchedBy:touch].center;
[self getBallTouchedBy:touch].center = CGPointMake(c.x+curr.x-prev.x, c.y+curr.y-prev.y);
}
// NOTE: This does not check the path between the moves, only the new point.
// So a fast movement "past" the other object will not get detected.
// If you want to do comple detection, use an existing engine
[self checkForCollisions];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
for (UITouch *touch in touches) {
[self stopTouch:touch];
}
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
[self touchesEnded:touches withEvent:event];
}
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
activeTouches = [NSMutableDictionary dictionary];
currentCollisions = [NSMutableSet set];
[self addBallWithRadius:20 point:CGPointMake(110, 100) color:[UIColor blueColor]];
[self addBallWithRadius:20 point:CGPointMake(200, 200) color:[UIColor redColor]];
[self addBallWithRadius:20 point:CGPointMake(235, 250) color:[UIColor greenColor]];
}
maybe this will help some, just a little modification to the code above.
-(CGFloat)CGPointDistance:(CGPoint)p1 p2:(CGPoint)p2
{
return sqrtf(powf(p1.x - p2.x) + powf(p1.y-p2.y));
}
-(BOOL)DidCollide:(View*)v1 v2:(View*)v2 v1Radius:(CGFloat)v1Radius v2Radius:(CGFloat)v2Radius
{
CGFloat r1 = v1.frame.size.width * v1Radius;
CGFloat r2 = v2.frame.size.width * v2Radius;
return [self PointDistance:v1.center p2:v2.center] < (r1 + r2)
}
and to check for collision your just do
if([self DidCollide:view1 v2:view2 v1Radius:radius v2Radius:radius])
{
//do something
}

Resources