blueHula
blueHula

Reputation: 1581

Using a delegate to communicate between a UIPopoverViewController and a MainViewController

I am working on an iPad app that has a main view (In this case an MKMapView) with a PopoverView controller which contains a UITableViewController. The content in the UITableview contains a number of variable elements which I need for dropping annotation pins on the MapView. These variables change depending on which row is selected. My thought was to use a delegate to accomplish this but I am struggling to implement it.

I have declared delegate in the showIncidentList method (see enclosed code). My questions are, is this custom delegate appropriate for this desired outcome, and if so, what am i missing that will allow me to communicate the information in the delegate and call the plotCallLocations method on the main view with the updated information from the delegate.

Relevent code:

SearchViewController.h //the popoverViewController class

@protocol IncidentPickerDelegate <NSObject>

- (void)incidentSelected:(NSMutableArray *)incidentDetail;

@end

@interface SearchViewController : UITableViewController  <UITableViewDelegate, UITableViewDataSource> { 
__weak id<IncidentPickerDelegate> _delegate;
}

@property (nonatomic, retain) NSMutableArray *incidentDetails;
@property (nonatomic, weak) id<IncidentPickerDelegate> delegate;
@end

SearchViewController.m

#import "TwitterSearchViewController.h"
#import "CallViewController.h"

@implementation SearchViewController

@synthesize delegate = _delegate;
@synthesize incidentDetails= _incidentDetails;

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {

_incidentDetails = [[NSMutableArray alloc] initWithObjects:[textItems objectAtIndex:0], [textItems objectAtIndex:1], lat, lon, nil];     //textItems is an NSArray of parsed JSON data, lat and lon are int's

 NSLog(@"_delegate = %@", _delegate);
        if ([_delegate respondsToSelector:@selector(incidentSelected:)]) {
            NSMutableArray *incidentDet = _incidentDetails;
            [_delegate incidentSelected:incidentDet];
 NSLog(@"incidentDetail ----> %@", incidentDet);
} 

@end

CallViewController.h //MainViewController

#import "SearchViewController.h"

@interface CallViewController : UIViewController <CLLocationManagerDelegate, MKMapViewDelegate, UIAlertViewDelegate, IncidentPickerDelegate> {
}

@property (nonatomic, retain) UIPopoverController *incidnetListPopover;
@property (nonatomic, retain) SearchViewController *incidentPicker;

-(IBAction)showIncidentList:(id)sender;

CallViewController.m

#import "CallViewController.h"
#import "SearchViewController.h"

@implementation CallViewController

@synthesize incidnetListPopover = _incidnetListPopover;
@synthesize incidentPicker = _incidentPicker;

UIStoryboard*  sb = [UIStoryboard storyboardWithName:@"MainStoryboard"
                                              bundle:nil];

if (_incidentPicker == nil) {
    self.incidentPicker = [sb instantiateViewControllerWithIdentifier:@"SearchViewController"];
    _incidentPicker.delegate = self;

    self.incidnetListPopover = [[UIPopoverController alloc] 
                                initWithContentViewController:_incidentPicker];               
}

[self.incidnetListPopover presentPopoverFromBarButtonItem:sender 
                                permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];

}


- (void)incidentSelected:(NSMutableArray *)incidentDetail {


//    for (id<MKAnnotation> annotation in _mapView.annotations) {
//      [_mapView removeAnnotation:annotation];

NSLog(@"plotCall called");
NSNumber * latitude = [incidentDetail objectAtIndex:2];
NSNumber * longitude = [incidentDetail objectAtIndex:3];
NSString * name = [incidentDetail objectAtIndex:1];
NSString * address = [incidentDetail objectAtIndex:0];
CLLocationCoordinate2D coordinate;
coordinate.latitude = latitude.doubleValue;
coordinate.longitude = longitude.doubleValue;  

CallLocation *annotation = [[CallLocation alloc] initWithName:name address:address coordinate:coordinate];
[_mapView addAnnotation:annotation];  

[self.incidnetListPopover dismissPopoverAnimated:YES];

}

Upvotes: 2

Views: 259

Answers (1)

user467105
user467105

Reputation:

The custom delegate approach is fine for this situation.

The main problem is that CallViewController does not implement the exact method specified in the IncidentPickerDelegate protocol which is incidentSelected:. (By the way, I assume "TwitterSearchViewController" is a typo and should be "SearchViewController" or vice versa.)

Even though CallViewController has the method plotCallLocations: which also takes an array, it is not named exactly incidentSelected: (it needs to be).

You should be getting some compiler warnings about this.

So when the SearchViewController calls that protocol method, it probably crashes with an "unrecognized selector" error.

Here are some solutions (first one is the easiest):

  • In CallViewController, change plotCallLocations: to incidentSelected:
  • In the protocol, change incidentSelected: to plotCallLocations:
  • In CallViewController, add a incidentSelected: method and from there, call plotCallLocations:


Separately (not causing an issue but), in SearchViewController, instead of checking if the delegate is nil, it's better to check if the delegate actually has the method you're about to call using respondsToSelector:.

To do this, first you'll have to make IncidentPickerDelegate implement the NSObject protocol:

@protocol IncidentPickerDelegate<NSObject>

Now in SearchViewController, instead of checking if the delegate is nil:

if ([_delegate respondsToSelector:@selector(incidentSelected:)])

Upvotes: 1

Related Questions