I’ve been following Big Nerd Ranch’s iOS Programming Guide (3rd Ed.) to set up my Xcode project which displays a list of my company’s products and then a detailed view for each.
I got the app working swimmingly the way I need it to, but I started running into trouble when I tried to fancy up the user experience. Adding a UISplitViewController for iPad has caused me no end of head aches and wasted afternoons.
At the moment I am getting semantic issues reported on my delegate-related code. One in DetailViewController.h and the other in ListViewController.m.
I’ll sum up my intent for this code before I post it, but in my inexperience I may miss some subtleties:
AppDelegate allocates UITableViewController (ListViewController class), and UIViewController (DetailViewController class) and then checks for an iPad. If an iPad, it creates a UISplitViewController using an array of the two views. Otherwise it loads ListViewController as the master view.
Before I tried to create the delegate relationship between the two views, the app was building successfully but the iPad UISplitViewController loaded only an empty detail view.
The iphone loaded ListViewController, then selecting a row displayed an empty detail view (DetailViewController). When you return to the TableView, and select the same or another table cell, the correct information would then load into DetailView. This led me to believe that the initial instance of the TableView was not passing on the selection correctly, but that returning to it (reallocating it?) would correct the problem. I was hoping the delegate setup would fix that. Since I can’t get that part working I can’t test that theory. I just figured I’d mention it.
I’ve looked around as much as I know how to (the right keywords and search terms elude me) with regards to UISplitViewController questions and tutorials, but they all vary greatly from what I’ve already set up in my project, either in the behavior of the app or the overall structure of the code. I’d rather not have to start over when I seem to be so close.
I’ve opened up the BigNerdRanch sample code (which does work) and, as I said, the only differences seem related to the way I want to display my information. At this point I need some help, please, to find what I’m doing wrong.
Thanks in advance!
AppDelegate.m:
#import "ProductFeedAppDelegate.h"
#import "ListViewController.h"
#import "DetailViewController.h"
@implementation ProductFeedAppDelegate
@synthesize window = _window;
@synthesize managedObjectContext = _managedObjectContext;
@synthesize managedObjectModel = _managedObjectModel;
@synthesize persistentStoreCoordinator = _persistentStoreCoordinator;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
ListViewController *lvc = [[ListViewController alloc] initWithStyle:UITableViewStylePlain];
UINavigationController *masterNav = [[UINavigationController alloc] initWithRootViewController:lvc];
DetailViewController *dvc = [[DetailViewController alloc] init];
[lvc setDetailViewController:dvc];
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
UINavigationController *detailNav = [[UINavigationController alloc] initWithRootViewController:dvc];
NSArray *vcs = [NSArray arrayWithObjects:masterNav, detailNav, nil];
UISplitViewController *svc = [[UISplitViewController alloc] init];
//set delegate
[svc setDelegate:dvc];
[svc setViewControllers:vcs];
[[self window] setRootViewController:svc];
} else {
[[self window] setRootViewController:masterNav];
}
self.window.backgroundColor = [UIColor whiteColor];
[self.window makeKeyAndVisible];
return YES;
}
//... trimmed out some template code to spare you
@end
`
ListViewController.h:
#import <Foundation/Foundation.h>
#import "ProductItemCell.h"
//#import "ItemStore.h"
#import "DetailViewController.h"
@class DetailViewController;
@class RSSChannel;
@interface ListViewController : UITableViewController
{
RSSChannel *channel;
}
@property (nonatomic, strong) DetailViewController *detailViewController;
-(void)fetchEntries;
@end
//A new protocol named ListViewControllerDelegate
@protocol ListViewControllerDelegate
//Classes that conform to this protocol must implement this method:
- (void)listViewController:(ListViewController *)lvc handleObject:(id)object;
@end
ListViewController.m:
#import "ListViewController.h"
#import "RSSChannel.h"
#import "RSSItem.h"
#import "DetailViewController.h"
#import "ContactViewController.h"
#import "FeedStore.h"
@implementation ListViewController
@synthesize detailViewController;
- (void)transferBarButtonToViewController:(UIViewController *)vc
{
// Trimming Code
}
- (id)initWithStyle:(UITableViewStyle)style
{
// Trimming Code
}
- (void)showInfo:(id)sender
{
// Create the contact view controller
ContactViewController *contactViewController = [[ContactViewController alloc] init];
if ([self splitViewController]) {
[self transferBarButtonToViewController:contactViewController];
UINavigationController *nvc = [[UINavigationController alloc]
initWithRootViewController:contactViewController];
// Create an array with our nav controller and this new VC's nav controller
NSArray *vcs = [NSArray arrayWithObjects:[self navigationController],
nvc,
nil];
// Grab a pointer to the split view controller
// and reset its view controllers array.
[[self splitViewController] setViewControllers:vcs];
// Make contact view controller the delegate of the split view controller
[[self splitViewController] setDelegate:contactViewController];
// If a row has been selected, deselect it so that a row
// is not selected when viewing the info
NSIndexPath *selectedRow = [[self tableView] indexPathForSelectedRow];
if (selectedRow)
[[self tableView] deselectRowAtIndexPath:selectedRow animated:YES];
} else {
[[self navigationController] pushViewController:contactViewController
animated:YES];
}
// Give the VC the channel object through the protocol message
// [channelViewController listViewController:self handleObject:channel];
}
- (void)viewDidLoad
{
// Trimming Code
}
- (NSInteger)tableView:(UITableView *)tableView
numberOfRowsInSection:(NSInteger)section
{
return [[channel items] count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// Trimming Code
}
- (void)fetchEntries
{
// Trimming Code
}
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if (![self splitViewController])
[[self navigationController] pushViewController:detailViewController animated:YES];
else {
[self transferBarButtonToViewController:detailViewController];
// We have to create a new navigation controller, as the old one
// was only retained by the split view controller and is now gone
UINavigationController *nav =
[[UINavigationController alloc] initWithRootViewController:detailViewController];
NSArray *vcs = [NSArray arrayWithObjects:[self navigationController],
nav,
nil];
[[self splitViewController] setViewControllers:vcs];
// Make the detail view controller the delegate of the split view controller
[[self splitViewController] setDelegate:detailViewController];
}
RSSItem *item = [[channel items] objectAtIndex:[indexPath row]];
// Next line reports: No visible @interface for 'DetailViewController' declares the selector 'listViewController:handleObject:'
[detailViewController listViewController:self handleObject:item];
}
@end
DetailViewController.h:
#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
#import "ListViewController.h"
@class RSSItem;
@class Reachability;
@interface DetailViewController : UIViewController <ListViewControllerDelegate> // Cannot find protocol declaration for 'ListViewControllerDelegate'
{
__weak IBOutlet UILabel *nameField;
__weak IBOutlet UITextView *descriptField;
__weak IBOutlet UIImageView *imageView;
__weak IBOutlet UITextView *introtextField;
__weak IBOutlet UIButton *dsButton;
__weak IBOutlet UIButton *aeButton;
__weak IBOutlet UIButton *imButton;
}
-(BOOL)reachable;
@property (nonatomic, strong) RSSItem *item;
@property (nonatomic, strong) UIImage *productImage;
@end
DetailViewController.m:
#import "DetailViewController.h"
#import "RSSItem.h"
#import "RSSChannel.h"
#import "Reachability.h"
@interface DetailViewController ()
@end
@implementation DetailViewController
- (void)listViewController:(ListViewController *)lvc handleObject:(id)object
{
//RSSItem *item = object; //This was in the example code but if left in the next line reported "Local declaration of 'item' hides instance variable"
// Validate the RSSItem
if (![item isKindOfClass:[RSSItem class]])
return;
[self setItem:item];
[[self navigationItem] setTitle:[item name]];
[nameField setText:[item name]];
[descriptField setText:[item descript]];
[introtextField setText:[item introtext]];
}
@synthesize item;
- (BOOL)reachable{
// Trimming Code
}
- (void)viewDidLoad
{
[super viewDidLoad];
[[self view] setBackgroundColor:[UIColor whiteColor]];
}
- (void)viewWillAppear:(BOOL)animated
{
if (item){
[super viewWillAppear:animated];
[nameField setText:[item name]];
[descriptField setText:[item descript]];
[introtextField setText:[item introtext]];
// Trimming Code (all the stuff that looks for this or that value and acts upon it)
} else {
// The following appears in the log:
NSLog(@"There's no item selected");
}
}
@end
I think you are running into a problem with the compiler getting confused by having several
If you remove this import from your ListViewController.h and keep the
Then I think this will get rid of your compiler problems.
You probably need to add
< UISplitViewControllerDelegate >to a couple of your other classes though. Looks like you are setting them as delegates on the split view but not adopting the protocol.