I need a dead simple implementation of MKCircle overlay - iphone

I have a small map on my view that I want to drop an MKCircle overlay onto. I have all the coords and radius as I am creating regions for monitoring. I would like to display this region back to the user so they will know what the boundaries are.
For the life of me, there isn't one good tutorial on the internet that will give me just the bare necessities to drop a circle onto my map and be done.
As a precursor... I have used Apple's examples with no luck. The Regions example is supposed to be the one need, but all I can get it to do is drop the pin, no circle. I even copied their classes directly into my project... no joy. So if you can either point me to a good example or layout exactly what needs to be implemented in a simple ViewController, I would be very grateful.

My guess for why using the sample code didn’t work: you didn’t hook up your view controller as the map view’s delegate. First step for doing that is making sure the controller implements the MKMapViewDelegate protocol, like this (in its header file):
#import <MapKit/MapKit.h>
#interface MyViewController : UIViewController <MKMapViewDelegate>
If you’re setting up the view controller from a XIB, Ctrl-drag from the map view to your controller instance and connect it as the map view’s delegate outlet. If you’re setting it up in code, then call theMapView.delegate = self; in your -loadView or -viewDidLoad.
Then, at some point (in your -viewDidLoad, for instance),
[theMapView addOverlay:[MKCircle circleWithCenterCoordinate:someCoordinate radius:someRadius]];
…will result in the map view calling its delegate’s -mapView:viewForOverlay: method, which you can implement something like this:
-(MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id <MKOverlay>)overlay {
MKCircleView *circleView = [[MKCircleView alloc] initWithCircle:(MKCircle *)overlay];
circleView.fillColor = [UIColor blueColor];
return [circleView autorelease];
}

It's
-(MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id <
MKOverlay>)overlay
for the full delegate method, see a complete answer for people completely lost like myself.

Related

How can I get a UIWebView's UIScrollView delegate methods called from within a standard UIViewController?

So I just have a standard UIViewController with a UIWebView in it that displays a pdf. For the app functionality, I have need to be able to respond to the UIWebView's nested UIScrollView events like scrollViewWillBeginDragging, scrollViewDidScroll, etc.
The only way I can get access to the scrollView is to (it seems like a hack) go in and get it by the subviews array:
for (id subview in webView.subviews){
if ([[subview class] isSubclassOfClass: [UIScrollView class]]) {
UIScrollView * s = (UIScrollView*)subview;
s.delegate = self;
s.tag = 1;
scrollView = s;
}
}
But that seems to introduce more problems than it's worth, because I lose native UIScrollView stuff like zooming.
So to sum up what I'm needing:
What is the best way to set my UIViewController class as the delegate of the UIScrollView inside of the UIWebView? Is there something I need to do with subclassing my own UIWebView so that it handles events from it's built in UIWebView a certain way and then pass it along somehow to the UIViewController? How does that passing thing work anyway?
Please advise!
Thank you!
Have you checked there is only one UIScrollView subclass in the subviews? Bung in a log in your loop to see. If there is more than one, then you'll only pick up the last one using your code.
If there is just one UIScrollView subclass, you could try saving a reference to its delegate and then in your own delegate methods passing messages on after you have done your business.
So, in your loop, something like
originalDelegate = s.delegate
And then for the delegate methods, something like:
- (void) scrollViewDidScroll: (UIScrollView*) scrollView;
{
// do your stuff
[originalDelegate scrollViewDidScroll: scrollView];
}
You might need to check whether originalDelegate responds to the selector before calling it, i.e. if ([originalDelegate respondsToSelector: #selector(scrollViewDidScroll:)) etc. If it were me, I'd start by implementing all twelve delegate methods defined in the UIScrollView delegate protocol.
Not tested this, so will be interested to know if it can be made to work. Do note, the docs explicitly say that UIWebView "should not be subclassed"

Show callout when tapping overlay

I have an MKMapView with several overlays. Works all just fine, and it's incredible how simple it works. There is, however, one thing I can't get to work. The idea is simple: when a user taps within the area that is covered by an overlay, a callout with some information about that overlay has to come up. The overlays are all MKPolygons, which follow the MKOverlay protocol and therefore the MKAnnotation protocol.
The MKOverlay protocol conforms to the
MKAnnotation protocol. As a result,
all overlay objects are also
annotation objects and can be
treated as one or both in your code.
If you opt to treat an overlay object
as both, you are responsible for
managing that object in two places. If
you want to display both an overlay
view and annotation view for it, you
must implement both the
mapView:viewForOverlay: and
mapView:viewForAnnotation: methods in
your application delegate. It also
means that you must add and remove the
object from both the overlays and
annotations arrays of your map.
This comes from the Apple docs. I tried something like this:
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation {
MKAnnotationView *aView = [[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:nil];
aView.canShowCallout = YES;
}
But that doesn't seem to work. I've tried using gesture recognizers, but I have no idea how to show a callout other than by using the canShowCallOut property...
I suppose you have to add the MKOverlays as annotations also
[self.mapView addAnnotations:myOverlays];
Then return a MKAnnotationView in (mapView:viewForAnnotation) that's not hidden, either a graphic (tap-able) or zero alpha view. Next, add a UITapGestureRecognizer for each MKOverlayView, make sure it works with the map's gestures (UIGestureRecognizerDelegate implementation for simultaneous recognition). Finally when your gesture recognizer fires do this
[self.mapView setSelectedAnnotations:[NSArray arrayWithObject:myOverlayView.overlay]];
I'm not certain that this actually triggers the callOut showing though.
Also make sure your return title and/or subtitle from your overlay object.

UIView, how to determine when touches entered the view

It appears that all the touch methods of a UIView are only called if the touches began within the bounds of that view. Is there a way to have a view respond to a user who has touched outside the view, but then dragged his fingers into the view?
In case it matters, my specific application is for dragging a MKPinAnnotationView (using built-in 4.0 dragging). I want something to happen if the user drags a pin onto another view (which happens to be an AnnotationView as well, but it could be anything). No method for dragging is called until I let go of the pin; and no method no the UIView that's being dragged to seems to be called unless I started by touching from within the view.
Because the superview is a MKMapView, it is difficult to just use the touchesMoved event of that and check if the user is in the right location or not. Thanks!
So after playing around with it for a while, I found that the answer given here actually gave me what I needed, even though the question being asked was different.
It turns out you can subclass UIGestureRecognizer; and have it handle all the touches for the view that it has been added to (including an MKMapView). This allows all the normal MKMapView interactions to still behave without any problem; but also alerts me of the touches. In touchesMoved, I just check the location of the touch; and see if it is within the bounds of my other view.
From everything I tried; this seems to be the only way to intercept touchesMoved while the user is dragging an MKAnnotation.
You sure can:
(HitstateView.h)
#import <UIKit/UIKit.h>
#interface HitstateView : UIView {
id overrideObject;
}
#property (nonatomic, retain) id overrideObject;
#end
(HitstateView.m)
#import "HitstateView.h"
#implementation HitstateView
#synthesize overrideObject;
- (void)dealloc {
self.overrideObject = nil;
[super dealloc];
}
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
UIView *hitView = [super hitTest:point withEvent:event];
if (hitView == self) {
return overrideObject;
}
return hitView;
}
#end
Make this view the size of your touch area. Set the overideObject to the view you want the touches to go. IIRC it ought to be a subview of the HitstateView.
Every view inherits UIResponder so every view gets touchesBegan/Moved/Ended - I do not think starting the touch outside the view means the view gets no event when the touch moves over the view. If you want to get a notification that something has been dragged onto your MKMapView you should make a subclass that handles the touch but then passes the event to super, allowing the hierarchy to do whatever it needs to do with the touch. You don't need to capture or modify the event just observe it.
It depends on how your views are set up. Generally leveraging the responder chain is the best way to go. It allows you to play tricks, though it may be too specific to address your particular needs.
You can also play tricks with forward events by override hit testing:
http://developer.apple.com/library/ios/#documentation/EventHandling/Conceptual/EventHandlingiPhoneOS/MultitouchEvents/MultitouchEvents.html%23//apple_ref/doc/uid/TP40009541-CH3-SW3
Your particular case sounds pretty exotic, so you may have to play tricks like having a parent view whose frame is large enough to contain both views in question.

MKMapView not calling delegate methods

In a UIViewController I add a MKMapView to the view controlled by the controller.
- (void)viewDidLoad {
[super viewDidLoad];
CGRect rect = CGRectMake(0, 0, 460, 320);
map = [[MKMapView alloc] initWithFrame:rect];
map.delegate = self;
[self.view addSubview:map];
}
Later in the controller I have
- (void)mapViewDidFinishLoadingMap:(MKMapView *)mapView
{
NSLog(#"done.");
}
Done never gets printed. None of the other delegate methods get called either like mapView:viewForAnnotation: I use a MKMapView in an another app, but this seems to happen on any new application I make. Has anyone else seen this behavior?
EDIT:
The problem seems to be when UIViewController is made the delegate of the MKMapView, a direct subclass of NSObject seems to work okay. I can work around like this, still seems very odd since I've done it before.
I had the exact same problem: I had assigned < MKMapViewDeledate> to my controller, and linked the delegate to "File owner" in Interface Builder, but still my implemented delegate methods were not called.
The reason is that since iOS 4, maps are cached, and whenever an app display a cached map then methods such as "mapViewDidFinishLoadingMap" are not called. Resetting "contents and settings" in the simulator clears the cached maps and therefore solves the issue.
A bit old, but since iOS 7 there is a new method that might work. solved my problem
- (void)mapViewDidFinishRenderingMap:(MKMapView *)mapView fullyRendered:(BOOL)fullyRendered
Perhaps you need to go to the IB and control-drag from the MKMapView to the view conroller, then select delegate to make it a delegate???
maybe quite obvious, but just checking:
Have you made sure your viewcontroller declaration is correctly done.
Something like:
#interface YourViewController : UIViewController <MKMapViewDelegate>
I had a similar problem with the methods of MKMapViewDelegate not being called.
My issue was setting the MKCoordinateRegionMakeWithDistance and regionThatFits in the controller's -viewDidLoad() I wanted to only show the region around my house and not start with the world view. So after adding annotations in the controller's viewDidLoad, I started a timer. When it expires in one second, I zoom in on the region I want with the above APIs and the delegate methods fire. It just makes me a little dizzy and inclined to vomit on my iPad.
Now I only have to deal with the darn low memory warnings.
I had a similar issue in XCode 6.1 building for iOS 8.1 where none of the MKMapViewDelegate methods were being called. In a 2nd application the identical code resulted in the MKMapViewDelegate methods being called as expected.
In ViewController.m the mapView delegate was set as follows:
- (void)viewDidLoad {
...
self.mapView.delegate = self;
In ViewController.h:
#interface myMapViewController : UIViewController <MKMapViewDelegate>
#property (strong, nonatomic) IBOutlet MKMapView *mapView;
#end
I had manually added the IBOutlet line above to the header file, but only in the app where the delegate methods were not being received.
The solution was to delete the IBOutlet line I had added manually to the header, and then go to the storyboard and CTRL-drag from the MKMapView to the #interface block in my ViewController.h. The identical IBOutlet was recreated in the header file with the result that all the delegate methods were called correctly.
I just ran into this again on iOS 7 and figured out something that works consistently. The issue we're typically fighting is calling setRegion before the region has context (view rendered). iOS 7 adds new delegate methods representing all tiles being rendered but pre-iOS 7 the cached tiles prevents the delegate method mapViewDidFinishLoadingMap from being called. Instead I use a "pendingRegion" variable to check if I have "queued" a region change for when the map is rendered and then simply use the viewDidAppear method as a signal the map has been rendered to the screen and hence a region will have context.
Add a variable to your header:
MKCoordinateRegion pendingRegion;
Add this method to your code:
-(void)initPendingRegion {
pendingRegion.center = CLLocationCoordinate2DMake(0.0, 0.0);
}
Add this to your viewDidAppear:
if(pendingRegion.center.latitude != 0.0) {
MKCoordinateRegion useRegion = pendingRegion;
[self initPendingRegion];
[self.mapView setRegion:useRegion animated:YES];
}
My problem is that I forget to set location in the simulator. Go to Simulator -> Debug -> Location -> Custom location... and the MKMapViewDelegate methods will start calling
For me, it worked after adding NSLocationWhenInUseUsageDescription string in Info.plist
I also met same issue as yours. I found that: with the iPhone SDK 3.2 - when I create a new UIViewController with an associated xib file (the checkbox option in UIViewController creating dialog), delegate methods of MKMapViewDelegate are never called.
However, when I follow below steps, it runs well.
Create a new UIViewController class (never check the option: create xib file associated with the controller)
Create a new xib file. Add the map using Interface Builder + setting the Owner class with the class on step 1 + setting delegate object of the map point to the owner class.
Implement delegate methods for the UIViewController class in step 1.
When I want to use my ViewController (with the added map), I load it with the nib name as below example:
MapPreviewController* mapPreviewController = [[MapPreviewController alloc]
initWithNibName:#"MapPreviewController" bundle:[NSBundle mainBundle]];
[self.centerPanelView addSubview:mapPreviewController.view];
The code runs ok. I don't know what Apple changes or did with the xib file when I create my UIViewController using the wizard, but it may the root cause of my problem. It takes me 1 day to find this stupid solution.
If you found another one, please share to me.
Thanks

How to programmatically launch/dispose of views for the iPhone?

I am not using a tab bar or any other control -- I want to basically change the current view to a different view controller dynamically (i.e. via code). I'm not using Interface Builder at all...
For example, let's say we create three view controllers:
(this may not be the best example, but I'm trying to make it simplistic)
View_Hello.m (and .h)
View_Goodbye.m (and .h)
View_Ciao.m (and .h)
Our ViewerAppDelegate would launch View01_Hello.
View_Hello would have a custom touch method that would then need to go to View_Ciao if swiped, but View_Goodbye if just touched.
Any ideas on how I could do this (and please don't say "oh, you need to use xxx interface element for this example". I need to be able to randomly change views based on programatic control within the view of the application I'm working on)
I'm been surfing through Google and StackOverflow for the past week, and gone through my three O'Reilly Cocoa books (plus three iPhone developer books), and all of them just use simple interfaces -- but nothing shows an example like what I'm trying to do.
===========
Edit (#Andrew Grant):
for example:
View_Ciao *viewCiao;
-(void) viewDidLoad {
viewCiao = [[View_Ciao alloc] initWithNibName:#"View_Ciao" bundle:nil];
[viewCiao.view setNeedsDisplay];
[super viewDidLoad];
}
This crashes. :-)
=============
Edit (#Daniel Dickison)
Brilliant -- that worked!
- (void)applicationDidFinishLaunching:(UIApplication *)application {
[window addSubview:viewController.view];
[window makeKeyAndVisible];
// create the second view, remove the first, and then display the second
viewCiao = [[ViewCiao alloc] init];
[viewController.view removeFromSuperview];
[window addSubview:viewCiao.view];
}
Have you seen recipe 2-8 "Swiping Views" in Erica Sadun's iPhone Developer's Cookbook? She uses touchesBegan and touchesMoved to determine swipe direction, then sets the animation based on the direction in touchesEnded.
You could extend or amend this to add tap detection in order to determine which view to transition to. The recipe is on or around page 69 if you'd like to have a look, anyway. There's also a video (mov) sample here and a code sample here.
How about something like:
#interface AppDelegate : ...
{
View_Hello *hello;
View_Goodbye *goodbye;
View_Ciao *ciao;
UIViewController *currentView;
UIWindow *window;
}
... IBOutlet properties for the 3 controllers.
#end
#implementation AppDelegate
- (void)switchToGoodbye
{
[currentView.view removeFromSuperview];
[window addSubview:goodbye.view];
currentView = goodbye;
}
... etc.
#end
You may need to resize the view before adding it to the window, and call viewWillDisappear:, viewDidDisappear:, viewWillAppear: and viewDidAppear: on currentView and the new view controller before and after they're removed/added.
Load the appropriate view and add it as a subview to the parent, then remove the existing view from its parent.
If you only have one level of views then your window is the parent.