I'm trying to implement a SplitViewController with a NavigationController in both master and detail. I have been following this tutorial, however I'm still running into a rather strange problem.
When I try to call the delegate method I get -[UINavigationController selectedStudent:]: unrecognized selector sent to instance...
Any help would be greatly appriciated.
Here's the code:
StudentSelectionDelegate.h
#import <Foundation/Foundation.h>
#class Student;
#protocol StudentSelectionDelegate <NSObject>
#required
-(void)selectedStudent:(Student *)newStudent;
#end
StudentDetail represents detail in the split view.
In StudentDetail.h I`ve got
#import "StudentSelectionDelegate.h"
#interface StudentDetail : UITableViewController <StudentSelectionDelegate>
...
StudentDetail.m
#synthesize SentStudent;
...
-(void)selectedStudent:(Student *)newStudent
{
[self setStudent:newStudent];
}
StudentList represents master of the splitview. In StudentList.h I`ve got :
#import "StudentSelectionDelegate.h"
...
#property (nonatomic,strong) id<StudentSelectionDelegate> delegate;
In StudentList.m in the didSelectRowAtIndexPath
[self.delegate selectedStudent:SelectedStudent];
And no "SelectedStudent" is not null
And finally AppDelegate.m
#import "AppDelegate.h"
#import "StudentDetail.h"
#import "StudentListNew.h"
...
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleLightContent];
UISplitViewController *splitViewController = (UISplitViewController *)self.window.rootViewController;
UINavigationController *leftNavController = [splitViewController.viewControllers objectAtIndex:0];
StudentListNew *leftViewController = (StudentListNew *)[leftNavController topViewController];
StudentDetail *rightViewController = [splitViewController.viewControllers objectAtIndex:1];
leftViewController.delegate = rightViewController;
return YES;
}
P.S. I have been searching for a solution for hours.
[splitViewController.viewControllers objectAtIndex:1] is a UINavigationController, not a StudentDetail.
The error message is telling you that UINavigationController does not have a selectedStudent property.
Your delegate is not pointing to a StudentDetail but a navigation controller, which doesn't even implement < StudentSelectionDelegate>. However, since you specified the cast, Objective C can't warn you that the object you cast isn't actually the kind of class that you cast it to be.
You should consider type checking the objects, as Apple's code does, to make sure that objects are the class you expect them to be.
Here's the corrected code:
UINavigationController *rightNavController = [splitViewController.viewControllers objectAtIndex:1];
StudentDetail *rightViewController = (StudentDetail *)[rightNavController topViewController];
leftViewController.delegate = rightViewController;
As for making sure that your delegate implements the method,
if ([self.delegate respondsToSelector:#selector(selectedStudent:)]) {
[self.delegate selectedStudent:SelectedStudent];
}
would have spared you from the exception, although you'd have to have used the debugger to realize that self.delegate wasn't a StudentDetail.
Related
i'm not new to OOP but i'm very new to objective c and i've been having trouble with reaching model from two different view controllers, one to set it the other to get the data. Here's my model:
#import <Foundation/Foundation.h>
#interface ModelUnit : NSObject{
NSString * nickname;
int total;
}
- (void)setTotal:(int)newTotal;
- (void)setName:(NSString *)nick;
- (NSString *)getName;
#end
#import "ModelUnit.h"
#implementation ModelUnit
- (void)setTotal:(int)newTotal{
total = newTotal;
}
- (void)setName:(NSString *)nick{
nickname = nick;
}
- (NSString *)getName{
return nickname;
}
#end
Here's how i try to set nickname in the initial viewcontroller:
//.h
#interface introViewController : UIViewController{
ModelUnit * modl;
}
-(ModelUnit *) modl;
-(IBAction)nickEntered:(UITextField *)sender;
#end
//.m
-(ModelUnit *) modl{
if(!modl){
modl = [[ModelUnit alloc] init];
}
return modl;
}
- (IBAction)nickEntered:(UITextField *)sender{
[[self modl] setName:[sender text]];
ViewController *vew = [[ViewController alloc] initWithNibName:nil bundle:nil];
[self presentViewController:vew animated:NO completion:Nil];
}
And here is how i try to receive and display it in the last viewcontroller:
- (void)viewDidLoad
{
[super viewDidLoad];
introViewController *pnt = [[introViewController alloc] initWithNibName:nil bundle:nil];
[display setText:[[pnt modl] getName]];
}
But it always prints it as null when i NSlog it to the console. I know it's a very novice question but i'm completely stuck. Thanks for any help.
It's because each of your UIViewController instances are referencing different instances of your model class (actually, a non-instance in the second case).
Make an #property in your "last" view controller (class name of "ViewController" it would seem), something like:
#property (strong, nonatomic) Model *model;
Then, when pushing your second view controller, set its model property, like this:
ViewController *vew = [[ViewController alloc] initWithNibName:nil bundle:nil];
vew.model = self.modl;
[self presentViewController:few animated:NO completion:NULL];
That way, you're passing around the same instance of your Model class. The way you're doing it, you're trying to access a property that was never initialized, which is why you're getting nil.
If you're not new to OOP, you should quite easily see the mistake you're making. You're trying to retrieve a value out of an object you just initialized.
Where you need to pass the data is in the first object as you initialized the second object.
Between this line:
ViewController *vew = [[ViewController alloc] initWithNibName:nil bundle:nil];
And this line:
[self presentViewController:vew animated:NO completion:Nil];
You're still in the first object and you hold a reference to the second object. Give the ViewController class (a better name and) a NSString property to hold the data the getName method returns. And then set that property between the two lines I posted.
Also, Objective-C doesn't name their getters with get.
I use this code, but "it works" doesn't happen.
DetailViewController.h
[#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
#protocol ProtocolNameDelegate
-(void)DoSomething;
#end
#interface DetailViewController : UIViewController {
id<ProtocolNameDelegate> _delegate;
}
#property (strong, nonatomic) id<ProtocolNameDelegate> _delegate;
DetailViewController.m
#synthesize _delegate;
- (void)viewDidLoad
{
[super viewDidLoad];
[_delegate DoSomething];
}
MasterViewController.h
#interface MasterViewController : UITableViewController <ProtocolNameDelegate>
MasterViewController.m
-(void)DoSomething
{
NSLog(#"It works");
}
I think i have to add something like:
MasterViewController* mvc = [[MasterViewController alloc] init];
mvc._delegate = self;
But it gives an error, and by the way will it create another instance of MasterViewController?
Instead of
MasterViewController* mvc = [[MasterViewController alloc] init];
mvc._delegate = self;
write this,
DetailViewController* svc = [[DetailViewController alloc] init];
dvc._delegate = self;
You made mistake in implementation.
Abstract of implementation should be.
Create Protocol in DetailVC.
Create Property for Delegate, Synthesize, and make call.
Import DetailVC in MasterVC and include delegate in MasterVC.h
Implement protocol method in MasterVC.m
Create instance of DetailVC and assign DetailVCObj.delegate = self;
In MasterViewController.m, you need to allocate and intialise DetailViewController somewhere
DetailViewController* dvc = [[DetailViewControlleralloc] init];
dvc._delegate = self;
Also, because you have written [_delegate doSomething] in
DetailviewController's viewDidLoad method,
it means you must set dvc._delegate = self; in MasterViewController.m
before loading dvc's view (before addSubview or anything that loads view).
Teaching myself iOS Programming, and starting by following this book.
I ran into error "Property 'MainViewController' not found on object of type 'AppDelegate *'.
I've double and triple checked that I followed the code correctly, even restarted from scratch. I've scoured StackOverflow and tried a few solutions but none worked and few properly match my issue. Any help?
AppDelegate.m (where the error lies)
#import "AppDelegate.h"
#import "WeatherForecast.h"
#import "MainViewController.h"
#implementation AppDelegate
#synthesize managedObjectContext = _managedObjectContext;
#synthesize managedObjectModel = _managedObjectModel;
#synthesize persistentStoreCoordinator = _persistentStoreCoordinator;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
WeatherForecast *forecast = [[WeatherForecast alloc] init];
self.MainViewController.forecast = forecast;
// Override point for customization after application launch.
MainViewController *controller = (MainViewController *)self.window.rootViewController;
controller.managedObjectContext = self.managedObjectContext;
return YES;
}
MainViewController.h
#import "FlipsideViewController.h"
#import "WeatherForecast.h"
#import <CoreData/CoreData.h>
#interface MainViewController : UIViewController <FlipsideViewControllerDelegate>
- (IBAction)showInfo;
- (IBAction)refreshView:(id) sender;
- (void)updateView;
#property (strong, nonatomic) NSManagedObjectContext *managedObjectContext;
#property (strong, nonatomic) WeatherForecast *forecast;
#end
The problem should be in your second line of application:didFinishLaunchingWithOptions. self.MainViewController is expecting a property in your AppDelegate. Just remove this line and add controller.forecast = forecast; before return YES. At this point you got a reference to your MainViewController and can set the property safely (assuming that MainViewController is set up as the current rootViewController through your Storyboard or XIB).
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
WeatherForecast *forecast = [[WeatherForecast alloc] init];
// Override point for customization after application launch.
MainViewController *controller = (MainViewController *)self.window.rootViewController;
controller.managedObjectContext = self.managedObjectContext;
controller.forecast = forecast;
return YES;
}
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
#import "DetailViewController.h"
If you remove this import from your ListViewController.h and keep the
#class DetailViewController;
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.
The delegate relationships were not set up 100% correctly. Here is how this was fixed.
In ListViewController.m, added a class extension:
#interface ListViewController() <UISplitViewControllerDelegate>
#end
In ListViewController.h, removed:
#import "DetailViewController.h"
In DetailViewController.h, changed line to:
#interface DetailViewController : UIViewController <ListViewControllerDelegate, UISplitViewControllerDelegate>
In ContactViewController.h, changed line to:
#interface ContactViewController : UIViewController <MFMailComposeViewControllerDelegate, UISplitViewControllerDelegate>
These things cleared all errors. This did not, as I'd hoped in my original post, correct the issue of my item not being passed to the detailViewController, as that problem was a result of using "item" instead of "object" in DetailViewController.m's handleObject statement.
I'm loading an application (via embedded framework) via a PhoneGap Plugin.
Now, I'm trying to pass the current UIViewController to the parentViewController property of the FlashizFacade object.
When I execute my application I'm getting this error in the debug console when assigning self:
2012-09-10 13:01:43.663 gTicketSales[2805:16a03] -[FlashizPlugin navigationController]: unrecognized selector sent to instance 0x91925e0
2012-09-10 13:01:43.665 gTicketSales[2805:16a03] ***WebKit discarded an uncaught exception in the webView:decidePolicyForNavigationAction:request:frame:decisionListener: delegate:<NSInvalidArgumentException>-[FlashizPlugin navigationController]: unrecognized selector sent to instance 0x91925e0
When assigning self.viewController:
2012-09-10 14:31:21.455 gTicketSales[3693:16a03] ***WebKit discarded an uncaught exception in the webView:decidePolicyForNavigationAction:request:frame:decisionListener: delegate:<Wrong parentViewController specified>When using non-modal display, parentViewController have to be an instance of UIViewController with a valid navigationController assigned
I've tried:
facade.parentViewController = self;
facade.parentViewController = self.viewController;
My .h file
#import <Cordova/CDVPlugin.h>
#import <FlashizPaymentLib/FlashizFacade.h>
#interface FlashizPlugin : CDVPlugin <FlashizPaymentDelegate> {}
#property (nonatomic, retain) FlashizFacade* flashizFacade;
- (void) openFlashiz:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options;
#end
My .m file
#import "FlashizPlugin.h"
#implementation FlashizPlugin
#synthesize flashizFacade;
- (void) openFlashiz:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options {
NSLog(#"Flashiz Payment Opening...");
NSString *callbackId = [arguments pop];
NSString *invoiceId = [arguments objectAtIndex:0];
FlashizFacade* facade = [[FlashizFacade alloc] initWithEnvironment:FE_TEST];
facade.parentViewController = self;
facade.delegate = self;
self.flashizFacade = facade;
[facade executePaymentForInvoiceId:invoiceId];
[facade release];
CDVPluginResult *result;
result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:#"Flashiz Payment Executed..."];
[self writeJavascript:[result toSuccessCallbackString:callbackId]];
}
#end
Thanks in advance!
On AppDelegate.h
#property (nonatomic, strong) UINavigationController *navigationController;
on AppDelegate.m change this line on application didFinishLaunchingWithOptions
self.window.rootViewController = self.viewController;
for this
UINavigationController *aNavigationController = [[UINavigationController alloc] initWithRootViewController:self.viewController];
self.navigationController = aNavigationController;
self.window.rootViewController = self.navigationController;
In my project is crashing because I don't have a ConnectionViewController, I think I didn't include the framework correctly, because I didn't know whato to do with the Resources folder, but I think it should work for you.
EDIT: I just included the resources folder, and it's working!!!
And if you don't want the navigationBar on the phonegap's view controller put in the viewDidLoad of MainViewController.m
self.navigationController.navigationBar.hidden = YES;