Silver Challenge- HELP with code


#1

I am trying to solve the silver challenge and am at a loss. Can someone scour my code and please let me know why I keep getting an exception when I press one of the segments on segmented control?

[code]// WhereamiViewController.h
// Whereami
//
// Created by Sean Greene on 10/29/12.
// Copyright © 2012 Sean Greene. All rights reserved.
//

#import <UIKit/UIKit.h>
#import <CoreLocation/CoreLocation.h>
#import <MapKit/MapKit.h>

@interface WhereamiViewController : UIViewController<CLLocationManagerDelegate, MKMapViewDelegate, UITextFieldDelegate>

{
CLLocationManager *locationManager;
IBOutlet MKMapView *worldView;
IBOutlet UIActivityIndicatorView *activityIndicator;
IBOutlet UITextField *locationTitleField;

}

//received the target-action from segmented control to switch the map type
-(IBAction)segControlClicked:(id)sender;

-(void)findLocation;
-(void)foundLocation:(CLLocation*)loc;
@end
[/code]

and in my .m file

[code] WhereamiViewController.m
// Whereami
//
// Created by Sean Greene on 10/29/12.
// Copyright © 2012 Sean Greene. All rights reserved.
//

#import “WhereamiViewController.h”
#import “BNRMapPoint.h”

@implementation WhereamiViewController

-(id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil

{
self=[super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
//create location manager object
locationManager=[[CLLocationManager alloc]init];

    [locationManager setDelegate:self];

    [locationManager setDesiredAccuracy:kCLLocationAccuracyBest];
    
    //Set distancefilter property
    [locationManager setDistanceFilter:1];
    
  
    
}


return self;

}

-(IBAction)segControlClicked:(id)sender
{
int clickedSegment=[sender selectedSegmentIndex];
switch (clickedSegment) {
case 0:
[worldView setMapType:MKMapTypeStandard];
break;
case 1:
[worldView setMapType:MKMapTypeSatellite];
case 2:
[worldView setMapType:MKMapTypeHybrid];
break;
}
}
-(void)locationManager:(CLLocationManager*)manager
didUpdateToLocation:(CLLocation*)newlocation
fromLocation:(CLLocation*)oldLocation
{
NSLog(@"%@",newlocation);
//How many seconds ago was this new location created?
NSTimeInterval t=[[newlocation timestamp]timeIntervalSinceNow];

//CLLocationManagers will return the last found location of the device first, you don`t want that data in this case. If this location was made more than 3 minutes ago, ignore it.
if (t<-180) {
    //This is cached data, you dont want it, keep looking
    return;
}
[self foundLocation:newlocation];

}

-(void)locationManager:(CLLocationManager*)manager
didFailWithError:(NSError *)error
{
NSLog(@“Could not find location: %@”,error);
}

-(void)locationManager:(CLLocationManager *)manager
didUpdateHeading:(CLHeading *)newHeading
{
NSLog(@"%@",newHeading);

}

-(void)viewDidLoad
{
[worldView setShowsUserLocation:YES];
}

-(void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation
{
//here we are, but how do we actually zoom?
CLLocationCoordinate2D loc=[userLocation coordinate];
MKCoordinateRegion region=MKCoordinateRegionMakeWithDistance(loc, 250, 250);
[worldView setRegion:region animated:YES];

}

-(BOOL)textFieldShouldReturn:(UITextField *)textField
{
//This method isnt implemented yet- but soon will be.
[self findLocation];
[textField resignFirstResponder];
return YES;
}

-(void)findLocation
{
[locationManager startUpdatingLocation];
[activityIndicator startAnimating];
[locationTitleField setHidden:YES];
}

-(void)foundLocation:(CLLocation *)loc
{
CLLocationCoordinate2D coord=[loc coordinate];

//create an instance of BNRMapPoint with the current data
BNRMapPoint *mp=[[BNRMapPoint alloc]initWithCoordinate:coord title:[locationTitleField text]];

//add the instance of BNRMapPoint to the map view
[worldView addAnnotation:mp];

//zoom the region to this location
MKCoordinateRegion region=MKCoordinateRegionMakeWithDistance(coord, 250, 250);
[worldView setRegion:region animated:YES];

//reset the UI
[locationTitleField setText:@""];
[activityIndicator stopAnimating];
[locationTitleField setHidden:NO];
[locationManager stopUpdatingLocation];

}
-(void)dealloc
{
//Tell the location manager to stop sending us messages
[locationManager setDelegate:nil];
}

@end
[/code]

I know the solution should be relatively easy but I am completely stuck. I am still a novice so please guide me with details of the solution/fix.


#2

What is the exception saying exactly?

Does it occur after the segControlClicked: method fires or before?

If you are not sure, put a log statement inside the segControlClicked: method:

-(IBAction)segControlClicked:(id)sender
{
    NSLog (@"---> %s: begin", __PRETTY_FUNCTION__);

    int clickedSegment=[sender selectedSegmentIndex];
    switch (clickedSegment) {
        case 0:
            [worldView setMapType:MKMapTypeStandard];
            break;
        case 1:
            [worldView setMapType:MKMapTypeSatellite];
        case 2:
            [worldView setMapType:MKMapTypeHybrid];
            break;
    }
    NSLog (@"---> %s: end", __PRETTY_FUNCTION__);
}

Build and run again to see if you can see the output from the log statements; the output should start with "—>".

Also, you are missing a break statement in the switch statement. Is that an intentional fall through?


#3

Hi ibex10,

I have inserted the log code and got the following:

2012-11-08 16:57:41.906 Whereami[6530:13d03] —> -[WhereamiViewController segControlClicked:]:begin
2012-11-08 16:57:41.920 Whereami[6530:13d03] —>-[WhereamiViewController segControlClicked:]:end
2012-11-08 16:57:41.921 Whereami[6530:13d03] -[WhereamiViewController segmentedControlAction:]: unrecognized selector sent to instance 0x80a0310

A breakpoint also occurs at this point:

[code]// main.m
// Whereami
//
// Created by Sean Greene on 10/29/12.
// Copyright © 2012 Sean Greene. All rights reserved.
//

#import <UIKit/UIKit.h>

#import “WhereamiAppDelegate.h”

int main(int argc, char *argv[])
{
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([WhereamiAppDelegate class]));
}
}[/code]


#4

Then, it looks like a method is being invoked which does not exist or which exists but its name is misspelled when being invoked.

Do you need to define the method segmentedControlAction:?


#5

ok here is my complete code. Try copy and pasting and see if you get the same error I do.

[code]#import <UIKit/UIKit.h>
#import <CoreLocation/CoreLocation.h>
#import <MapKit/MapKit.h>

@interface WhereamiViewController : UIViewController<CLLocationManagerDelegate, MKMapViewDelegate, UITextFieldDelegate>

{
CLLocationManager *locationManager;
IBOutlet MKMapView *worldView;
IBOutlet UIActivityIndicatorView *activityIndicator;
IBOutlet UITextField *locationTitleField;

}

//received the target-action from segmented control to switch the map type
-(IBAction)setMapType:(id)sender;

-(void)findLocation;
-(void)foundLocation:(CLLocation*)loc;
@end[/code]

[code]#import “WhereamiViewController.h”
#import “BNRMapPoint.h”

@implementation WhereamiViewController

-(id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil

{
self=[super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
//create location manager object
locationManager=[[CLLocationManager alloc]init];

    [locationManager setDelegate:self];

    [locationManager setDesiredAccuracy:kCLLocationAccuracyBest];
    
    //Set distancefilter property
    [locationManager setDistanceFilter:1];
    
  
    
}


return self;

}

-(void)locationManager:(CLLocationManager*)manager
didUpdateToLocation:(CLLocation*)newlocation
fromLocation:(CLLocation*)oldLocation
{
NSLog(@"%@",newlocation);
//How many seconds ago was this new location created?
NSTimeInterval t=[[newlocation timestamp]timeIntervalSinceNow];

//CLLocationManagers will return the last found location of the device first, you don`t want that data in this case. If this location was made more than 3 minutes ago, ignore it.
if (t<-180) {
    //This is cached data, you dont want it, keep looking
    return;
}
[self foundLocation:newlocation];

}

-(void)locationManager:(CLLocationManager*)manager
didFailWithError:(NSError *)error
{
NSLog(@“Could not find location: %@”,error);
}

-(void)locationManager:(CLLocationManager *)manager
didUpdateHeading:(CLHeading *)newHeading
{
NSLog(@"%@",newHeading);

}

-(void)viewDidLoad
{
[worldView setShowsUserLocation:YES];

//Silver Challenge

}

-(void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation
{
//here we are, but how do we actually zoom?
CLLocationCoordinate2D loc=[userLocation coordinate];
MKCoordinateRegion region=MKCoordinateRegionMakeWithDistance(loc, 250, 250);
[worldView setRegion:region animated:YES];

}

-(BOOL)textFieldShouldReturn:(UITextField *)textField
{
//This method isnt implemented yet- but soon will be.
[self findLocation];
[textField resignFirstResponder];
return YES;
}

-(void)findLocation
{
[locationManager startUpdatingLocation];
[activityIndicator startAnimating];
[locationTitleField setHidden:YES];
}

-(void)foundLocation:(CLLocation *)loc
{
CLLocationCoordinate2D coord=[loc coordinate];

//create an instance of BNRMapPoint with the current data
BNRMapPoint *mp=[[BNRMapPoint alloc]initWithCoordinate:coord title:[locationTitleField text]];

//add the instance of BNRMapPoint to the map view
[worldView addAnnotation:mp];

//zoom the region to this location
MKCoordinateRegion region=MKCoordinateRegionMakeWithDistance(coord, 250, 250);
[worldView setRegion:region animated:YES];

//reset the UI
[locationTitleField setText:@""];
[activityIndicator stopAnimating];
[locationTitleField setHidden:NO];
[locationManager stopUpdatingLocation];

}

-(void)dealloc
{
//Tell the location manager to stop sending us messages
[locationManager setDelegate:nil];
}

-(IBAction)setMapType:(id)sender
{
switch (((UISegmentedControl *)sender).selectedSegmentIndex)
{
case 0:
{
[worldView setMapType:MKMapTypeStandard];
}

    case 1:
    {
        [worldView setMapType:MKMapTypeSatellite];
    }
    default:
{
    [worldView setMapType:MKMapTypeHybrid];
}
}

}

@end
[/code]

still get a thread break right here at this code whenever I press one of the segmented control buttons:

int main(int argc, char *argv[]) { @autoreleasepool { return UIApplicationMain(argc, argv, nil, NSStringFromClass([WhereamiAppDelegate class])); } }

At this point I am out of ideas. Could someone please try running my code as written?


#6

OK 3 things. And let me say I have about 3 years Cocoa (desktop) experience.

  1. your switch statement is sort of messed up.
    this:
    switch (((UISegmentedControl *)sender).selectedSegmentIndex)

should be
switch ([sender selectedSegmentIndex])
because, you don’t need to cast/declare the sender as a UISegControl. It already is that. And you are not “making” the control so it’s not like you need to declare it. The dot notation is fine to use but going by book convention I used bracket notation. it could also be written just
switch (sender.selectedSegmentIndex)

  1. I think that in the XIB there was a connection to the segmented control in my project. If you click on the segmented control to highlight it, then in the Utility pane on the right, look in the Binding section (little arrow pointing to the right). Mine had “Value Changed” linked to “File’s Owner, Style Changed” plus also “File’s Owner, setMapType”. So it was trying to run 2 methods when it was clicked. I removed the StyleChanged from the binding by pressing the tiny X next to that one.

  2. Switch should use break; after each statement or else it will do all the other things below it. If you do the first to 2 fixes but not add break; you’ll see when the app starts you may have the map view, but then the next time you press a button, any one of them, you get hybrid view. Because switch is running but it does map view, then satellite, then hybrid every time. you need “break” to stop going down the chain of cases. It sounds dumb that it wouldn’t just jump out on it’s own, but that’s how it works. (The interesting thing is you can chain together cases which always happen with ones that only sometimes happen by breaking or not breaking in certain lines).

HTH,
Chris