How to know when a child view of a UIViewController is removed? - ios

I'm using the TapJoy SDK for a game application on iOS. The SDK has a way to display a view on top of the application: http://knowledge.tapjoy.com/integration-8-x/ios/pb/featured-app
I can give the function a UIVIewController argument, so I can manage the show/hide by myself.
I have created the following UIViewVontroller:
#interface MyViewController : UIViewController
- (void) viewDidLoad;
- (void) viewDidUnload;
- (void) viewWillLoad;
- (void) viewWillUnload;
- (void)viewWillAppear:(BOOL)animated;
- (void)viewDidAppear:(BOOL)animated;
- (void)viewWillDisappear:(BOOL)animated;
- (void)viewDidDisappear:(BOOL)animated;
#end
#implementation MyViewController
- (void) viewDidLoad
{
self.view = GRAPHIC_SYSTEM::GetGlView();
NSLog(#"viewDidLoad");
}
- (void) viewDidUnload
{
NSLog(#"viewDidUnload");
}
- (void) viewWillLoad
{
NSLog(#"viewWillLoad");
}
- (void) viewWillUnload
{
NSLog(#"viewWillUnload");
}
- (void)viewWillAppear: (bool)animated
{
NSLog(#"viewWillAppear");
}
- (void)viewDidAppear:(BOOL)animated
{
NSLog(#"viewDidAppear");
}
- (void)viewWillDisappear:(BOOL)animated
{
NSLog(#"viewWillDisappear");
}
- (void)viewDidDisappear:(BOOL)animated
{
NSLog(#"viewDidDisappear");
}
#end
When I'm notified by TapJoy that a feature app is available, I show it using my view controller:
[TapjoyConnect showFeaturedAppFullScreenAdWithViewController: [[MyViewController alloc] init]];
The TapJoy view is successfully displayed on top of my game.
There are 2 problems:
Only the viewDidLoad log is printed in the console. None of the other log messages are printed
I would like to know when the user has closed the TapJoy view, so I can add some processing at that time, but none of the other functions of the view controller are called.
I've seen here on SO that some users recommend to use the Notifications. Unfortunately, as I don't have access to the source code of the TapJoy SDK, I need to find another way.
Do you have any ideas?
Thanks in advance
Mike

Well I could fix the issue by creating a custom UIView, which I set as the UIViewController view.
Next, I have then overriden the willRemoveSubview function of this custom view.
And with the viewDidLoad function of the UIViewController, I know when the view is displayed, AND when the TapJoy view is removed, so I can remove my custom view too.

Related

Protocol being called in two different classes at same time

I am a little new to iOS development, coming from a Java / Android background. My understanding is that your custom Protocols or Delegates are like Interfaces in Java land.
If that is the case then I believe these Protocols are also Objects as well.
Case:
Assume 2 ViewControllers, Home and Profile.
1 Presenter, let's call it StuffPresenter gets instantiated individually in both ViewControllers.
StuffPresenter has an initialization method called initWithInteractor that takes in a parameter of Interactor which is a protocol.
Both Home and Profile implement a Protocol called Interactor, which has a method called initStuffInTableView(NSMutableArray *)stuff.
So I have a dilemma where if I am in Home and StuffPresenter relays information then I switch over to Profile, StuffPresenter loads stuff in Home as well as Profile.
Why is this the case?
Here is the code I have setup:
Protocol
#protocol Interactor <NSObject>
- (void)initStuffInTableView:(NSMutableArray *)stuff;
#end
Presenter
#interface Presenter : NSobject
- (id)initWithInteractor:(id<Interactor>)interactor;
- (void)loadStuff;
#end
#implementation {
#private
id<Interactor> _interactor;
}
- (id)initWithInteractor:(id<Interactor>)interactor {
_interactor = interactor;
return self;
}
- (void)loadStuff {
// Load stuff
NSMutableArray *stuff = // Init stuff in array...
[_interactor initStuffInTableView:stuff];
}
#end
Home
#interface HomeViewController : UITableViewController <Interactor>
- (void)initPresenter;
#end
#implementation {
#private
StuffPresenter *_stuffPresenter;
}
- (void)viewDidLoad {
[super viewDidLoad];
[self initPresenter];
[self initPullToRefresh];
}
# pragma mark - init
- (void)initPresenter {
_stuffPresenter = [[StuffPresenter alloc] initWithInteractor:self];
}
- (void)initPullToRefresh {
// Init pull to refresh
// ...
[self.refreshControl addTarget:self
action:#selector(reloadStuff)
forControlEvents:UIControlEventValueChanged];
}
# pragma mark - Interactor
- (void)initStuffInTableView:(NSMutableArray *)stuff {
// Do some work
[self.tableView reloadData];
}
# pragma mark - reloadStuff
- (void)reloadStuff {
[_stuffPresenter loadStuff];
}
# pragma mark - TableView methods here
// TableView methods...
#end
Profile
#interface ProfileViewController : UITableViewController <Interactor>
- (void)initPresenter;
#end
#implementation {
#private
StuffPresenter *_stuffPresenter;
}
- (void)viewDidLoad {
[super viewDidLoad];
[self initPresenter];
}
# pragma mark - init
- (void)initPresenter {
_stuffPresenter = [[StuffPresenter alloc] initWithInteractor:self];
[_stuffPresenter loadStuff];
}
# pragma mark - Interactor
- (void)initStuffInTableView:(NSMutableArray *)stuff {
// Do some work
[self.tableView reloadData];
}
# pragma mark - TableView methods here
// TableView methods...
#end
Problem:
When I go to Profile the app crashes, because initStuffInTableView is being called in Home. Why is this the case?
A protocol is an Objective-C language feature for specifying that a Class (or another protocol) has certain features, for the benefit of the compiler/ARC/the programmer.
A delegate, or delegation, is a design pattern which makes Model View Controller easier. To make the object doing the delegation be more flexible, generally its delegate adopts a protocol.
There are a number of issues in your code:
Your Presenter has a reference cycle with its interactors
You need to call some init method that eventually calls [super init] in your Presenter's initWithInteractor: method.
As others have pointed out, your methods which begin with init violate Objective-C conventions.
It's hard to tell from what you've posted exactly what your problem is, but I'm very suspicious of how it's structured.
You have a single class (Presenter), which you make two instances of, and pass no parameters other than the Interactor.
How could each instance know to load different "stuff" based on which View controller it received as a parameter?
I still want to understand thoroughly what is going on here, but I did solve the problem by doing the following.
In both Home and Profile, I init the StuffPresenter here:
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animation];
[self initPresenter];
}
And when exiting a controllerview I do the following:
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
_stuffPresenter = nil;
}

How can I handle a button click on Apple Watch?

I have a button in my main interface. I did a ctl + drag from the button in the storyboard to the InterfaceController.m I then put an NSLog in the resulting method. This is what the code looks like
#import "InterfaceController.h"
#interface InterfaceController()
#end
#implementation InterfaceController
- (void)awakeWithContext:(id)context {
[super awakeWithContext:context];
NSLog(#"awakeWithContext!");
// Configure interface objects here.
}
- (IBAction)Alarm {
NSLog(#"Alarm!");
}
- (void)willActivate {
// This method is called when watch view controller is about to be visible to user
[super willActivate];
NSLog(#"willActivate!");
}
- (void)didDeactivate {
// This method is called when watch view controller is no longer visible
[super didDeactivate];
NSLog(#"didDeactivate!");
}
#end
I also took a screenshot with the logs appearing. No log occurs when a I press the Alarm button in the emulator.
UPDATE
I just followed the example in this video.
"How to connect a WKInterfaceButton to an action in the code using Xcode" https://www.youtube.com/watch?v=CuCuL-a608w
I added this to the header - (IBAction)alarmPressed:(id)sender;
I put this in the view controller
- (IBAction)alarmPressed:(id)sender
{
NSLog(#"alarmPressed!");
}
I dragged the sent action select to the Interface Controller and selected alarmPressed. This still does not produce logs when pressed. Also traits includes "User Interaction Enabled"
Unfortunately the watchKit doesn't support the :(id)sender argument. so your IBAction should just read
-(IBAction)alarmPressed{
NSLog(#"alarmPressed!"):
}
also make sure that this dot is filled in. if it isn't that means you need to click on the hollow dot and drag it to your button.

How to display Full Screen iad for ipad

I just recently read that full screen iAd only works on iPad. ADInterstitialAd This Class To Use Developing Full Screen iAd. How To Implement Delegate Methodes in ADInterstitialAdDelegate?
You can use this for creating Full screen iAd
in .H file
Import
#import <iAd/iAd.h>
#interface ViewController : UIViewController<ADInterstitialAdDelegate>
{
ADInterstitialAd *interstitial;
}
in. M file
#interface ViewController ()
// Interstitials
- (void)cycleInterstitial;
#implementation ViewController
- (void)cycleInterstitial
{
// Clean up the old interstitial...
interstitial.delegate = nil;
// and create a new interstitial. We set the delegate so that we can be notified of when
interstitial = [[ADInterstitialAd alloc] init];
interstitial.delegate = self;
}
#pragma mark ADInterstitialViewDelegate methods
// When this method is invoked, the application should remove the view from the screen and tear it down.
// The content will be unloaded shortly after this method is called and no new content will be loaded in that view.
// This may occur either when the user dismisses the interstitial view via the dismiss button or
// if the content in the view has expired.
- (void)interstitialAdDidUnload:(ADInterstitialAd *)interstitialAd
{
[self cycleInterstitial];
}
// This method will be invoked when an error has occurred attempting to get advertisement content.
// The ADError enum lists the possible error codes.
- (void)interstitialAd:(ADInterstitialAd *)interstitialAd didFailWithError:(NSError *)error
{
[self cycleInterstitial];
}
// if you want to show iAd while pushing view controller just use
- (IBAction)onSearchClick:(id)sender {
if (interstitial.loaded) {
// [interstitial presentFromViewController:self]; // deprecated in iOS 6.
[self requestInterstitialAdPresentation]; // it will load iAD Full screen mode.
}
// do whatever you want to do.
}
Have a happy coding. Cheers.

Create a log in screen on iOS that will always show on top of all views

EDIT: Check below for solution.
I am working on a login screen for my app and I have it working for the most part except for a few edge cases. Ive set things up so that I have a segue from my UITabBar in the story board that I trigger in the app delegate applicationDidBecomeActive: method. As I said it works fine on all but one edge case I've found so far.
My app uses some modal view controllers, some of which are UIActivityViewControllers if that makes a difference, to enter and edit some core data entities. If one of these modal view controllers is opened when the app goes to the background, it will always show up when the app is reopened and my login doesn't show. I get the following console msg
Warning: Attempt to present <UINavigationController: 0x1d51e320> on <MPTabBarViewController: 0x1d5b4810> which is already presenting <UIActivityViewController: 0x1e38fc40>
Here is my code
- (void) displayLogin{
NSLog(#"%s", __PRETTY_FUNCTION__);
UITabBarController *tabBarController = (UITabBarController *)self.window.rootViewController;
NSDate *lastDate = [[NSUserDefaults standardUserDefaults] objectForKey:MPLastCloseDate];
NSTimeInterval timeDiff = [[NSDate date] timeIntervalSinceDate:lastDate];
int seconds = timeDiff;
if ([[NSUserDefaults standardUserDefaults] integerForKey:MPPassCodeDelay] == MPScreenLockAlways || seconds >= 300) {
NSLog(#"Should see login");
[tabBarController performSegueWithIdentifier:#"loginScreen" sender:self];
}
}
I understand exactly what this msg is telling me, the tab bar is already presenting a modal controller so it can't present another one. So my question is this, Is there a better way to implement this so that the login will always show, even over top of the modal views?
Okay here is my current solution
as suggested by Bartu and requested to be shared by Shawn
I have a working singleton loginManager class that requires 1 call in app delegate and 1 call in any view controller that could be called to present as modal. I was unable to figure out how to do this as suggested with a ViewController category, but hey a few includes and method calls aren't so bad. I included it in App-Prefix.pch, so its available everywhere. It is written for ARC, so if you like managing your own memory you'll need to modify the singleton for that. The last caveat, at current you will need to roll your own viewController for the login screen. Just look for the commented section in the implementation with all the stars, and put your own view controller there. Mine is still in my app storyboard, its basically 4 digit pin that checks for a match in the keychain and dismisses itself for the correct pin. I may pull that out of my storyboard and nib it so it could be packaged with the loginManager and let it become my first gitHub project at some future date though.
You can configure it to display login for every time the app opens or after a delay with properties. The delay time is also a property set in seconds. It will also block out your apps UI for the few seconds it takes to get the login displayed with a splash using your apps Default.png. This is also configurable with a property.
I would love to get some feedback on this, and if anyone can tell me how to do a category so the extra call in viewControllers is not needed that would be great! Enjoy!
AppDelegate:
- (void)applicationDidBecomeActive:(UIApplication *)application
{
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
[self.window makeKeyAndVisible];
// these calls are all optional
[[VHLoginManager loginManager] setShouldBlockUIWithSplashOnResume:NO];
[[VHLoginManager loginManager] setSecondsRequiredToPassBeforeLockDown:1000];
[[VHLoginManager loginManager] setScreenLockRequirment:VHLMScreenLockDelayed];
// this is the only required call to run with defaults - always login and block UI with splash while login loads
[[VHLoginManager loginManager] presentLogin];
}
Any viewController that may presented as modal at some point
- (void)viewDidLoad
{
[super viewDidLoad];
[[VHLoginManager loginManager] registerViewControllerIfModal:self];
}
The loginManager class
header:
// VHLoginManager.h
// Created by Victor Hudson on 5/31/13.
// Copyright (c) 2013 Victor Hudson. All rights reserved.
// Use if you like but be nice and leave my name
#import <Foundation/Foundation.h>
#define VHLMLastCloseDate #"VHLMLastCloseDate"
#define VHLMPassCodeDelay #"VHLMPassCodeDelay"
typedef enum {
VHLMScreenLockAlways = 0,
VHLMScreenLockDelayed = 1,
} VHLMScreenLockRequirement;
#interface VHLoginManager : NSObject
#property (nonatomic) BOOL shouldBlockUIWithSplashOnResume;
// defaults to yes so app contents arent visible before the login screen appears
#property (nonatomic) int secondsRequiredToPassBeforeLockDown;
// defaults to 5 minutes (300)
#pragma mark - Class Methods
+ (VHLoginManager *)loginManager;
// returns the singleton login manager
#pragma mark - Manager Methods
- (void) presentLogin;
// will determine if login should be presented an do so if needed
- (void) registerViewControllerIfModal:(UIViewController *)controller;
// any view controllers that are presented modally should call this with self as controller in viewDidLoad - the pupose of this manager is so login shows even over top of modals
- (void) setScreenLockRequirment:(VHLMScreenLockRequirement) requirement;
// deafaults to always if not adjusted
#end
implementation:
// VHLoginManager.m
// Created by Victor Hudson on 5/31/13.
// Copyright (c) 2013 Victor Hudson. All rights reserved.
// Use if you like but be nice and leave my name
#import "VHLoginManager.h"
static VHLoginManager *loginManager = nil;
#interface VHLoginManager ()
#property (nonatomic, strong) UIViewController *currentModalViewController;
#property (nonatomic) VHLMScreenLockRequirement screenLockrequirement;
#end
#implementation VHLoginManager
#pragma mark - Manager Methods
- (void) presentLogin
{
// NSLog(#"%s", __PRETTY_FUNCTION__);
if ([[NSUserDefaults standardUserDefaults] integerForKey:VHLMPassCodeDelay] == VHLMScreenLockAlways || [self timeSinceLastClose] >= self.secondsRequiredToPassBeforeLockDown) {
//NSLog(#"User should see login");
// determine who the presenting view controller should be
UIViewController *viewController;
if (self.currentModalViewController && self.currentModalViewController.presentingViewController != nil) {
// NSLog(#"We have a modal view controller on top");
viewController = self.currentModalViewController;
} else {
// NSLog(#"We have NO modal view controller on top");
// get the root view controller of the app
viewController = [[[UIApplication sharedApplication] keyWindow] rootViewController];
}
//********************************************************************************************************************************************************************************
// *** This is still tied into my app storyboard and should be made into a viewcontroller with nib to be portable with loginManager for now implement and present your own loginViewController
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard_iPhone" bundle:nil];
UINavigationController *navController = [storyboard instantiateViewControllerWithIdentifier:#"appLoginScreen"];
//********************************************************************************************************************************************************************************
// present the login to user
[viewController presentViewController:navController animated:NO completion:nil];
}
}
- (void) setScreenLockRequirment:(VHLMScreenLockRequirement) requirement
{
_screenLockrequirement = requirement;
[[NSUserDefaults standardUserDefaults] setInteger:self.screenLockrequirement forKey:VHLMPassCodeDelay];
}
- (void) registerViewControllerIfModal:(UIViewController *)controller
{
// NSLog(#"%s", __PRETTY_FUNCTION__);
if (controller.presentingViewController) {
NSLog(#"Registering a modalViewController");
self.currentModalViewController = controller;
}
}
#pragma mark - Private Methods
- (void) timeStampForBackground
{
// NSLog(#"%s", __PRETTY_FUNCTION__);
[[NSUserDefaults standardUserDefaults] setObject:[NSDate date] forKey:VHLMLastCloseDate];
[self setDisplaySplashForBackgroundResume];
}
- (int) timeSinceLastClose
{
return [[NSDate date] timeIntervalSinceDate:[[NSUserDefaults standardUserDefaults] objectForKey:VHLMLastCloseDate]];
}
#pragma mark Splash Screen management
- (void) setDisplaySplashForBackgroundResume
{
// NSLog(#"%s", __PRETTY_FUNCTION__);
if (self.shouldBlockUIWithSplashOnResume) {
// dismiss all keyboards and input views
UIView *topView = [[[[UIApplication sharedApplication] keyWindow] subviews] lastObject];
[topView endEditing:YES];
// Don't show a splash screen if the application is in UIApplicationStateInactive (lock/power button press)
UIApplication *application = [UIApplication sharedApplication];
if (application.applicationState == UIApplicationStateBackground) {
UIImageView *splash = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"Default"]];
splash.frame = application.keyWindow.bounds;
[application.keyWindow addSubview:splash];
}
}
}
- (void) removeSplashScreen
{
// NSLog(#"%s", __PRETTY_FUNCTION__);
if (self.shouldBlockUIWithSplashOnResume) { // we should have a splash image up if true
// so remove it
UIWindow *thewindow = [[UIApplication sharedApplication] keyWindow];
if ([[thewindow subviews] count] > 1) {
[NSThread sleepForTimeInterval:1.0];
[[[thewindow subviews] lastObject] removeFromSuperview];
}
}
}
#pragma mark - Class Management
//prevent additional instances
+ (id)allocWithZone:(NSZone *)zone
{
return [self loginManager];
}
+ (VHLoginManager *)loginManager
{
if (!loginManager) {
//Create The singleton
loginManager = [[super allocWithZone:NULL] init];
}
return loginManager;
}
- (id) init
{
// If we already have an instance of loginManager
if (loginManager) {
//Return The Old One
return loginManager;
}
self = [super init];
if (self) {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(timeStampForBackground)
name:UIApplicationDidEnterBackgroundNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(removeSplashScreen)
name:UIApplicationDidBecomeActiveNotification
object:nil];
self.shouldBlockUIWithSplashOnResume = YES;
self.secondsRequiredToPassBeforeLockDown = 300;
if (![[NSUserDefaults standardUserDefaults] integerForKey:VHLMPassCodeDelay]) {
[self setScreenLockRequirment:VHLMScreenLockAlways];
}
}
return self;
}
#end
I had the same problem a short time ago, my solution for this problem is to have a reference to any modal view which is currently presented in your app delegate. So, you can know if your tab bar controller is already presenting a modal controller and if it's the case, you can present your login view over your current modal view.
What I did was to have a switch in my appDelegate. when the app started, if the user had not logged in, I created the login view and make it the window's rootViewController. When the user successfully logged in, I used an animation block to set that view's alpha to 0, then created a UITabBarController, populated it, made it the window's rootViewController (with an alpha of 0, then animated it's alpha to 1). Worked really well. Not sure how to do this with storyboards though.
EDIT: now getting familiar with storyboards. So what you would do is not use the Main.storyboard per se (remove it from info.plist), then add a LoginViewController as a view, and have your UITabbarController there too - but nothing is the initial view controller. You obviously have to name each view so you can create it in code, but asking the Storyboard to create such and such a view controller
So in App Delegate, if logged in, instantiate the tab bar controller and add it as the root view controller. If the user has not logged in, create the LoginView and add it as rootview controller. If the user does login, have some method on the LoginViewController so it can ask the delegate to switch to the tab bar controller.

why is peoplePickerDelegate not getting address of the delegate right?

The scenario is that I have more than one view that wants to invoke the Address Book. So as not to duplicate the code of the delegate in each view I have located the code in the App Delegate's header and .m file, but using an "#interface AddressBookDelegate" and "#implementation AddressBookDelegate" at the bottom of the 2 respective App Delegate fiies-
#interface AddressBookDelegate : UIViewController <ABPeoplePickerNavigationControllerDelegate> {
AddressBookDelegate *addressBookDelegate;
}
#property (nonatomic, retain) AddressBookDelegate *addressBookDelegate;
#end
and
#implementation AddressBookDelegate
#synthesize addressBookDelegate;
- (void)peoplePickerNavigationControllerDidCancel: (ABPeoplePickerNavigationController *)peoplePicker
{
[self dismissModalViewControllerAnimated:YES];
}
- (BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)person
{
[super dismissModalViewControllerAnimated:YES];
...get stuff from the Address Book...
return NO;
}
- (BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)person
property:(ABPropertyID)property identifier:(ABMultiValueIdentifier)identifier
{
return NO;
}
Then in my views I have the following code:
addressBookDelegate = (AddressBookDelegate *) [[UIApplication sharedApplication] delegate];
ABPeoplePickerNavigationController *abPicker = [[ABPeoplePickerNavigationController alloc]init];
abPicker.peoplePickerDelegate = self.addressBookDelegate;
[self presentModalViewController:abPicker animated:YES];
[abPicker release];
The Address Book displays fine in all views. But when I take any user action that would invoke a delegate, like the Address Book's Cancel button, I crash-
-[MyprogAppDelegate peoplePickerNavigationControllerDidCancel:]: unrecognized selector sent to instance
It compiles clean, no Warnings.
How do I wire-up the peoplePickerDelegate to connect to the Address Delegate code when it is not physically in the same file as the view itself ? Thx.
ADDED NOTE: when I use the debugger and stop on the line
abPicker.peoplePickerDelegate = addressBookDelegate;
in the view code, I see that the address for the addressBookDelegate is stated to be the address of the MyprogAppDelegate, not AddressBookDelegate as I might have expected. That makes me think the displacement to the address book delegate code is off within the App Delegate file.
If the AddressBookDelegate Cancel Delegate code were say 1000 bytes into the AddressBookDelegate, my app is actually "entering" the code 1000 bytes into MyprogAppDelegate, and so crashes. So somehow I am not setting up the addressing of the AddressBookDelegate correctly. That's my take on it anyway...
Your code assumes that your appdelegate (MyprogAppDelegate) implements method peoplePickerNavigationControllerDidCancel.
So, your code in MyprogAppDelegate should be something like this:
#implementation MyprogAppDelegate
#synthesize ...;
#pragma mark -
#pragma mark Application lifecycle
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
return YES;
}
- (void)peoplePickerNavigationControllerDidCancel:(ABPeoplePickerNavigationController *)peoplePicker{
}
EDIT Okay, the entire first answer has been tossed out. This is, with some warning, still a bit of a shot in a dark, but I think it's going to be closer to helpful. Some of the ideas do carry over though.
You very probably don't need a separate class to act as your ABPeoplePickerNavigationControllerDelegate. In all likelihood, it should be the same class that has your code at the bottom (that calls presentModalViewController:animated:. Since I don't know what controller that was, I'm going to just call it MyViewController for reference. The reason you want that view controller to be the delegate is because, in your delegate methods, you need to be able to dismiss the modal view controller that has the address book.
You definitely don't want the your program's UIApplicationDelegate to be the ABPeoplePickerNavigationControllerDelegate. As you said yourself, peoplePickerDelegate has to be a UIViewController.
So, to MyViewController. First, the interface:
/* MyViewController.h */
#interface MyViewController : UIViewController<ABPeoplePickerNavigationControllerDelegate>
...
#end
Your controller might inherit from a descendant of UIViewController (like a table view controller or something like that) - that shouldn't change, the only thing that should change is adding the ABPeoplePickerNavigationControllerDelegate to the list of implemented protocols.
Now, to implement the functionality:
/* MyViewController.m */
#implementation MyViewController
...
- (void) whateverMethodIsDisplayingTheAddressBook
{
ABPeoplePickerNavigationController *abPicker = [[ABPeoplePickerNavigationController alloc]init];
abPicker.peoplePickerDelegate = self; // This view controller is the delegate
[self presentModalViewController:abPicker animated:YES];
[abPicker release];
}
...
- (void)peoplePickerNavigationControllerDidCancel: (ABPeoplePickerNavigationController *)peoplePicker
{
[self dismissModalViewControllerAnimated:YES];
}
- (BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker
shouldContinueAfterSelectingPerson:(ABRecordRef)person
{
[super dismissModalViewControllerAnimated:YES];
...get stuff from the Address Book...
return NO;
}
- (BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker
shouldContinueAfterSelectingPerson:(ABRecordRef)person
property:(ABPropertyID)property
identifier:(ABMultiValueIdentifier)identifier
{
return NO;
}
#end
In the end I was not able to make any of the above suggestions perform as hoped for. I had to cut time and move on so I duplicated the code in each view. I will revisit this another time, as I am sure it can be done in a more object based way than I ended it doing it.

Resources