I have a "problem" with the following code. The error message is: Application tried to push a nil view controller on target UINavigationController:0x10b82bbf0 when clicking on annotation #4's callout.
How can I make it open an alertview without getting this error?
//Callout button action
- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view
calloutAccessoryControlTapped:(UIControl *)control{
if ([view.annotation isKindOfClass:[Annotation class]])
{
Annotation *myAnn = (Annotation *)view.annotation;
id vcToPush = nil;
if ([[myAnn title] isEqualToString:#"1. Annotation one"]){
vcToPush = [[FirstViewController alloc]init];
}
if ([[myAnn title] isEqualToString:#"2. Annotation two"]){
vcToPush = [[SecondViewController alloc]init];
}
if ([[myAnn title] isEqualToString:#"3. Annotation three"]){
vcToPush = [[ThirdViewController alloc]init];
}
if ([[myAnn title] isEqualToString:#"4. Annotation four"]){
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Fourth annotation" message:#"Message" delegate:nil cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alertView show];
}
[self.navigationController pushViewController:vcToPush animated:YES];
}
}
//Custom callout
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)myAnn {
//Current location blue dot
if ([myAnn isKindOfClass:[MKUserLocation class]])
{
((MKUserLocation *)myAnn).title = #"My position";
return nil;
}//
MKPinAnnotationView *pinView = (MKPinAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:#"pinView"];
if (!pinView) {
pinView = [[MKPinAnnotationView alloc] initWithAnnotation:myAnn reuseIdentifier:#"pinView"];
pinView.pinColor = MKPinAnnotationColorRed;
pinView.canShowCallout = YES;
pinView.rightCalloutAccessoryView = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
}
else
{
pinView.annotation = myAnn;
}
return pinView;
}
Thank you!
In the if block
if ([[myAnn title] isEqualToString:#"4. Annotation four"]){
vcToPush is not initialize. Therefore is has the value it had before the method got called (nil in this case).
I assume you only want to show an alert in case of #4. In this case, add a return in the if block:
if ([[myAnn title] isEqualToString:#"4. Annotation four"]){
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Fourth annotation" message:#"Message" delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alertView show];
return;
}
Related
I tired to get indexpath of my custom annotation ,
I use this code for get my indexpath
NSUInteger index =[mapView.annotations indexOfObject:view.annotation];
its not really working because in my map, i got right lat and lang but not get the true data of pinview
pusingpalagw is my subclass
here is my code:
-(void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)view
{
// [(UIImageView *)view.leftCalloutAccessoryView setImageWithURL:[NSURL URLWithString:self.content.MERCHANT_IMAGE] usingActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite];
//if ([view.annotation isKindOfClass:[pusingpalagw class]]) {
// pusingpalagw *annot = view.annotation;
//NSInteger index = [self.arrayOfAnnotations indexOfObject:annot];
NSUInteger index =[mapView.annotations indexOfObject:view.annotation];
if (!self.content) {
pusingpalagw *calloutView = [[pusingpalagw alloc] initWithFrame:CGRectMake(0.0, 0.0, 242.0, 57.0)];
self.content12 = [self.listContent objectAtIndex:index];
calloutView.titleLabel.text = self.content12.MERCHANT_NAME;
calloutView.subtitleLabel.text = self.content12.MERCHANT_NAME;
//UIView *rating2 = (UIView*)[cell2 viewWithTag:110];
_starRating = [[EDStarRating alloc]initWithFrame:CGRectMake(calloutView.viewRating.frame.origin.x-15, calloutView.viewRating.frame.origin.y,80,20)];
_starRating.backgroundColor = [UIColor clearColor];
self.starRating.starImage = [UIImage imageNamed:#"kuningstarkosong"];
self.starRating.starHighlightedImage = [UIImage imageNamed:#"kuningstarfull"] ;
_starRating.maxRating = 5.0;
_starRating.delegate = self;
_starRating.horizontalMargin = 15.0;
_starRating.editable=NO;
_starRating.rating= [self.content12.MERCHANT_RATTING floatValue];
_starRating.displayMode=EDStarRatingDisplayHalf;
[_starRating setNeedsDisplay];
[calloutView.viewRating addSubview:_starRating];
NSLog(#"keluarbintang%#",_starRating);
NSURL *url = [NSURL URLWithString:self.content12.MERCHANT_IMAGE];
NSData *imageData = [NSData dataWithContentsOfURL:url];
UIImage *abs = [UIImage imageWithData:imageData];
[calloutView.imageMap setImage:abs];
//[calloutView.buttonDetail setTitle: #"Post" forState: UIControlStateNormal];
UIButton *buttonAja = [[UIButton alloc]initWithFrame:CGRectMake(0, 0, calloutView.frame.size.width, calloutView.frame.size.height)];
[buttonAja setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
//[buttonAja setTitle: #"Post" forState: UIControlStateNormal];
buttonAja.titleLabel.font = [UIFont systemFontOfSize:13.0];
[buttonAja addTarget:self action:#selector(goDetail:) forControlEvents:UIControlEventTouchUpInside];
[calloutView addSubview:buttonAja];
calloutView.center = CGPointMake(CGRectGetWidth(view.bounds) / 2.0, 0.0);
[view addSubview:calloutView];
}
//To get permission
if([CLLocationManager locationServicesEnabled]){
NSLog(#"Location Services Enabled");
if([CLLocationManager authorizationStatus]==kCLAuthorizationStatusDenied){
alert = [[UIAlertView alloc] initWithTitle:#"App Permission Denied"
message:#"To re-enable, please go to Settings and turn on Location Service for this app."
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];
}
}
//To set user Location
- (void)viewDidLoad
{
[super viewDidLoad];
self.mapView.showsUserLocation=YES;
self.mapView.delegate = self;
[self.mapView setUserTrackingMode:MKUserTrackingModeFollow animated:YES];
}
...
-(void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation
{
MKCoordinateRegion mapRegion;
mapRegion.center = mapView.userLocation.coordinate;
mapRegion.span.latitudeDelta = 0.2;
mapRegion.span.longitudeDelta = 0.2;
[mapView setRegion:mapRegion animated: YES];
}
Permission for IOS 8 Or Later :-
#define IS_OS_8_OR_LATER ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8.0)
if ([CLLocationManager locationServicesEnabled] )
{
if (self.locationManager == nil )
{
self.locationManager.delegate = (id)self;
self.locationManager = [[CLLocationManager alloc] init];
#ifdef __IPHONE_8_0
if(IS_OS_8_OR_LATER)
{
// Use one or the other, not both. Depending on what you put in info.plist
[self.locationManager requestWhenInUseAuthorization];
[self.locationManager requestAlwaysAuthorization];
}
#endif
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest;
self.locationManager.distanceFilter = kDistanceFilter; //kCLDistanceFilterNone// kDistanceFilter;
}
[self.locationManager startUpdatingLocation];
self.mapViewForPlace.showsUserLocation = YES;
[self.locationManager setDelegate:self];
NSLog(#"Location Title Is: %#", self.mapViewForPlace.userLocation.title);
}
Add info.plist NSLocationWhenInUseUsageDescription type of string.
-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
#ifdef __IPHONE_8_0
if(IS_OS_8_OR_LATER) {
// Use one or the other, not both. Depending on what you put in info.plist
[self.locationManager requestWhenInUseAuthorization];
[self.locationManager requestAlwaysAuthorization];
}
#endif
self.mapViewForPlace.showsUserLocation = YES;
self.currentLocation = [locations lastObject];
// here we get the current location
NSLog(#"Current Locations : %#",self.currentLocation);
// CLLocation* location = (CLLocation*)locations.lastObject;
// Use Apple's Geocoder to figure the name of the place
CLGeocoder* geoCoder = [[CLGeocoder alloc] init];
[geoCoder reverseGeocodeLocation:self.currentLocation completionHandler: ^(NSArray* placemarks, NSError* error) {
if (error != nil) {
NSLog(#"Error in geo coder: %#", error);
}
else {
if (placemarks.count == 0) {
NSLog(#"The address couldn't be found");
}
else {
// Get nearby address
CLPlacemark* placemark = placemarks[0];
// Get the string address and store it
NSString* locatedAt = [[placemark.addressDictionary valueForKey:#"FormattedAddressLines"] componentsJoinedByString:#", "];
//location.name = locatedAt;
currentLocationNameIs=locatedAt;
NSLog(#"The address is: %#", locatedAt);
}
}
}];
}
- (void) locationManager:(CLLocationManager *) manager didUpdateToLocation:(CLLocation *) newLocation fromLocation:(CLLocation *) oldLocation
{
self.currentLocation = newLocation;
self.mapViewForPlace.showsUserLocation = YES;
NSLog(#"New location Cordinate : %f %f",self.mapViewForPlace.userLocation.coordinate.latitude,self.mapViewForPlace.userLocation.coordinate.longitude);
[self.locationManager stopUpdatingLocation];
}
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation
{
// If it's the user location, just return nil.
if ([annotation isKindOfClass:[MKUserLocation class]])
return nil;
// Handle any custom annotations.
if ([annotation isKindOfClass:[MKPointAnnotation class]])
{
// Try to dequeue an existing pin view first.
MKAnnotationView *pinView = (MKAnnotationView*)[mapView dequeueReusableAnnotationViewWithIdentifier:#"CustomPinAnnotationView"];
if (!pinView)
{
// If an existing pin view was not available, create one.
pinView = [[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:#"CustomPinAnnotationView"];
//pinView.animatesDrop = YES;
pinView.canShowCallout = YES;
pinView.image = [UIImage imageNamed:#"facebook30.png"];
pinView.calloutOffset = CGPointMake(0, 32);
// Add a detail disclosure button to the callout.
UIButton* rightButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
pinView.rightCalloutAccessoryView = rightButton;
// Add an image to the left callout.
UIImageView *iconView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"facebook30.png"]];
pinView.leftCalloutAccessoryView = iconView;
} else {
pinView.annotation = annotation;
}
return pinView;
}
return nil;
}
I'm trying to call plotParkingLots from my AppDelegate as I want it to be called first when the app starts (and then every x seconds which I will implement later).
plotParkingLots works fine if I call it through my MapViewController's viewDidLoad, but when I call it from AppDelegate, it doesn't work.
I know that it is called but when I switch to my map view, no annotation is shown!
MapViewController.h
#import <UIKit/UIKit.h>
#import <MapKit/MapKit.h>
#define METERS_PER_MILE 1609.344
#interface MapViewController : UIViewController <MKMapViewDelegate>
#property (strong, nonatomic) IBOutlet MKMapView *mapView;
- (void)plotParkingLots;
#end
MapViewController.m
- (void)plotParkingLots {
NSString *url = [NSString stringWithFormat:#"http:/localhost/testes/parking.json"];
NSData *jsonData = [NSData dataWithContentsOfURL: [NSURL URLWithString:url]];
if (jsonData == nil) {
UIAlertView *alertBox = [[UIAlertView alloc] initWithTitle: #"Erro de conexão" message: #"Não foi possível retornar os dados dos estacionamentos" delegate: self cancelButtonTitle: #"Ok" otherButtonTitles: nil];
[alertBox show];
}
else {
NSError *error;
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:&error];
NSArray *parkings = [json objectForKey:#"parkings"];
for (NSDictionary * p in parkings) {
Parking *annotation = [[Parking alloc] initWithDictionary:p];
[_mapView addAnnotation:annotation];
}
}
}
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation {
static NSString *identifier = #"Parking";
if ([annotation isKindOfClass:[Parking class]]) {
MKPinAnnotationView *annotationView = (MKPinAnnotationView *) [_mapView dequeueReusableAnnotationViewWithIdentifier:identifier];
if (annotationView == nil) {
annotationView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:identifier];
} else {
annotationView.annotation = annotation;
}
annotationView.enabled = YES;
annotationView.canShowCallout = YES;
annotationView.image=[UIImage imageNamed:#"car.png"];
return annotationView;
}
return nil;
}
AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.
[[MapViewController alloc] plotParkingLots];
return YES;
}
You're not talking to the MapViewController that is displayed to the user. In your AppDelegate you momentarily allocation a MapViewController, plot the parking lots, then abandon that MapViewController and never use it again.
You'll need to keep a handle to that controller so that when the user presses something to make that viewcontroller get displayed it is the same one that has just plotted the parking lots.
i have been trying to copy text input from alertview textfield to a NSMutableArray that i will use later, alertview pops up and i enter the input to text field but when i press OK alert view disappears but doesnt copy text to my mutable array
here is my code
-(IBAction)add:(UIButton *)sender
{
addCustomStand = [[NSMutableArray alloc] init];
UIAlertView* dialog = [[UIAlertView alloc] initWithTitle:#"Enter a Stand Location"
message:#" "
delegate:self
cancelButtonTitle:#"Cancel"
otherButtonTitles:#"OK", nil];
UITextField *nameField = [[UITextField alloc]
initWithFrame:CGRectMake(20.0, 45.0, 245.0, 25.0)];
[nameField setBackgroundColor:[UIColor whiteColor]];
nameField.text = #"";
[dialog addSubview:nameField];
if ([nameField text]){
NSLog(#"Name Field %# ",nameField.text);
[addCustomStand addObject:nameField.text];
}
[nameField release];
[dialog show];
[dialog release];
}
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
NSString *title = [alertView buttonTitleAtIndex:buttonIndex];
if([title isEqualToString:#"OK"])
{
NSLog(#"Button 1 was selected.");
NSLog(#"StandLocations %# ",addCustomStand);
}
}
here is my output on log screen
2012-02-07 20:26:57.315 Avicii[1399:b603] Name Field
2012-02-07 20:26:59.720 Avicii[1399:b603] Button 1 was selected.
2012-02-07 20:26:59.721 Avicii[1399:b603] StandLocations (
""
)
anyone can help whats wrong with that code?
That's because [nameField text] doesn't have user entered value yet when you added it in your [addCustomStand addObject:nameField.text];
so change your adding into array in UIAlertView delegate method.
-(IBAction)add:(UIButton *)sender
{
addCustomStand = [[NSMutableArray alloc] init];
UIAlertView* dialog = [[UIAlertView alloc] initWithTitle:#"Enter a Stand Location"
message:#" "
delegate:self
cancelButtonTitle:#"Cancel"
otherButtonTitles:#"OK", nil];
UITextField *nameField = [[UITextField alloc]
initWithFrame:CGRectMake(20.0, 45.0, 245.0, 25.0)];
[nameField setBackgroundColor:[UIColor whiteColor]];
nameField.text = #"";
// Note at this line
nameField.tag = 100;
//
[dialog addSubview:nameField];
[nameField release];
[dialog show];
[dialog release];
}
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
NSString *title = [alertView buttonTitleAtIndex:buttonIndex];
if([title isEqualToString:#"OK"])
{
// Note at this line
UITextField* nameField = (UITextField *)[alertView viewWithTag:100];
[addCustomStand addObject:nameField.text];
//
NSLog(#"Button 1 was selected.");
NSLog(#"StandLocations %# ",addCustomStand);
}
}
You are adding nameField.text to your addCustomStand array before you even show the alert dialog. At the time you add it to the array the value is an empty string.
Instead you need to copy the value into your array during your clickedButtonAtIndex: method, by doing something like this:
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
NSString *title = [alertView buttonTitleAtIndex:buttonIndex];
if([title isEqualToString:#"OK"])
{
NSString *location;
UIView *view;
for (view in [alertView subviews]) {
if ([view isKindOfClass:[UITextField class]]) {
location = [(UITextField*)view text];
}
}
if (location) {
[addCustomStand addObject:location];
}
}
}
i'm just playing around with Apple's CurrentAddress sample code and I was trying to use the trueHeading property to determine the direction the user is facing. While that proved simple enough, I wanted to display a transparent PNG on top of the current location dot and I wanted to rotate it in order to simulate a compass.
Here's the very basic code I've currently got:
#implementation MapViewController
#synthesize mapView, reverseGeocoder, getAddressButton;
- (void)viewDidLoad
{
[super viewDidLoad];
mapView.showsUserLocation = YES;
}
- (IBAction)reverseGeocodeCurrentLocation
{
self.reverseGeocoder =
[[[MKReverseGeocoder alloc] initWithCoordinate:mapView.userLocation.location.coordinate] autorelease];
reverseGeocoder.delegate = self;
[reverseGeocoder start];
}
- (void)reverseGeocoder:(MKReverseGeocoder *)geocoder didFailWithError:(NSError *)error
{
NSString *errorMessage = [error localizedDescription];
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Cannot obtain address."
message:errorMessage
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alertView show];
[alertView release];
}
- (void)reverseGeocoder:(MKReverseGeocoder *)geocoder didFindPlacemark:(MKPlacemark *)placemark
{
PlacemarkViewController *placemarkViewController =
[[PlacemarkViewController alloc] initWithNibName:#"PlacemarkViewController" bundle:nil];
placemarkViewController.placemark = placemark;
[self presentModalViewController:placemarkViewController animated:YES];
}
- (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation
{
// we have received our current location, so enable the "Get Current Address" button
[getAddressButton setEnabled:YES];
NSString *north = [NSString stringWithFormat:#"%f", self.mapView.userLocation.heading.trueHeading];
NSLog(#"here: %#", north);
}
#end
How can I overlay the PNG to the exact position of the blue dot and keep it there (following the dot if the user moves, that is)?
The point is that you need to implement mapView:viewForAnnotation: delegate method and look inside it for annotation objects which are not yours. Take a look at the code snipped:
- (MKAnnotationView *)mapView:(MKMapView *)map viewForAnnotation:(id <MKAnnotation>)annotation {
NSString static *defaultID = #"defaultID";
NSString static *userLocationID = #"userLocationID";
if ([annotation isKindOfClass:[MyAnnotation class]]) {
// your code here
} else {
MKAnnotationView *annotationView = [map dequeueReusableAnnotationViewWithIdentifier:userLocationID];
if (!annotationView) {
annotationView = [[[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:userLocationID] autorelease];
annotationView.image = [UIImage imageNamed:#"UserLocationIcon.png"];
}
return annotationView;
}
}
I am displaying a pin on the map but I am not able to customize the display of the annotation view. For some reason my viewForAnnotation is not being called. Here is didFinishLaunchingWithOptions method.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[mapView setDelegate:self];
locationManager = [[CLLocationManager alloc] init];
[locationManager setDelegate:self];
[locationManager setDistanceFilter:kCLDistanceFilterNone];
[locationManager setDesiredAccuracy:kCLLocationAccuracyBest];
[mapView setShowsUserLocation:YES];
// Override point for customization after application launch.
[self.window makeKeyAndVisible];
return YES;
}
And here is my viewForAnnotation method which is never being called.
- (MKAnnotationView *)mv:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation
{
NSLog(#"viewForAnnotation");
if([annotation isKindOfClass:[MKUserLocation class]])
return nil;
static NSString *annotationIdentifier = #"AnnotationIdentifier";
MKPinAnnotationView *pinView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:annotationIdentifier];
[pinView setPinColor:MKPinAnnotationColorGreen];
pinView.animatesDrop = YES;
pinView.canShowCallout = YES;
UIImageView *houseIconView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"house.png"]];
pinView.leftCalloutAccessoryView = houseIconView;
[houseIconView release];
return pinView;
}
and here is didUpdateUserLocation method:
- (void)mapView:(MKMapView *)mv didUpdateUserLocation:(MKUserLocation *)userLocation
{
NSTimeInterval t = [[[userLocation location] timestamp] timeIntervalSinceNow];
if(t < -180) return;
NSLog(#"%#",[textField text]);
MapPoint *mp = [[MapPoint alloc] initWithCoordinate:userLocation.location.coordinate title:[textField text]];
[mv addAnnotation:mp];
[mp release];
}
The viewForAnnotation delegate method must be named mapView:viewForAnnotation:.
Your method is named mv:viewForAnnotation:.
Edit:
Here is an example with the two other changes suggested in my comments:
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation
{
NSLog(#"viewForAnnotation");
if([annotation isKindOfClass:[MKUserLocation class]])
return nil;
static NSString *annotationIdentifier = #"AnnotationIdentifier";
MKPinAnnotationView *pinView = (MKPinAnnotationView *) [mapView
dequeueReusableAnnotationViewWithIdentifier:annotationIdentifier];
if (!pinView)
{
pinView = [[[MKPinAnnotationView alloc]
initWithAnnotation:annotation
reuseIdentifier:annotationIdentifier] autorelease];
[pinView setPinColor:MKPinAnnotationColorGreen];
pinView.animatesDrop = YES;
pinView.canShowCallout = YES;
UIImageView *houseIconView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"house.png"]];
pinView.leftCalloutAccessoryView = houseIconView;
[houseIconView release];
}
else
{
pinView.annotation = annotation;
}
return pinView;
}