Two UIPopovers in One View - objective-c

I am trying to put two different UIPopovers in one view. I'm fairly new to objective-c, and programming in general, so instead of doing the smart, efficient method of having one popover and changing it's contents depending on how it is called, I just used the stupid, simple method of just creating two views, two delegates, two popovers etc... I don't know if that's why I'm having a problem, or if it's for some other reason.
So here's the problem. In the viewdidload of the view where the popovers appear, I have this code:
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
optionsViewController =[[OptionsViewController alloc]init];
optionsViewController.delegate = self;
popoverController = [[UIPopoverController alloc] initWithContentViewController:optionsViewController];
popoverController.popoverContentSize = CGSizeMake(320, 216);
[popoverController setDelegate:self];
newCurrencyViewController =[[newCurrencyViewController alloc]init];
newCurrencyViewController.delegate = self;
newCurrencyPopoverController = [[UIPopoverController alloc] initWithContentViewController:newCurrencyViewController];
newCurrencyPopoverController.popoverContentSize = CGSizeMake(320, 216);
[newCurrencyPopoverController setDelegate:self];
}
Obviously optionsViewController is the vc that appears inside popover 1 (with popover controller called "popoverController"), and newCurrencyViewController is the vc that appears inside popover 2 (with popover controller called "newCurrencyPopoverController").
Every time the view loads, the app crashes with a SIGABRT error, and the console says:
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[UIPopoverController initWithContentViewController:] must not be called with `nil`.'
Also, there's a warning saying "instance method -alloc not found (return type defaults to id)" for the line that
saysnewCurrencyViewController =[[newCurrencyViewController alloc]init];
My first thought was that I had misspelled the name of a file somewhere, since I think the problem is that it isn't finding the file called newCurrencyPopoverController, but I've checked everything and can't find any misspellings or anything. Any ideas?
Thanks very much!
LUKE

You are calling methods alloc + init of your variable newCurrencyViewController but you should call them to the class of that variable!
Line with bug:
newCurrencyViewController =[[newCurrencyViewController alloc]init];
The result of this line will be newCurrencyViewController == nil. And when you will try to init UIPopoverController with that view it will crash as you described.
If variable newCurrencyViewController is of class, for example, CurrencyViewController then you should replace that line with this one:
newCurrencyViewController =[[CurrencyViewController alloc] init];

you dont have an object to call alloc and init on when you are calling newCurrenceViewController.
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
optionsViewController =[[OptionsViewController alloc]init];
optionsViewController.delegate = self;
popoverController = [[UIPopoverController alloc] initWithContentViewController:optionsViewController];
popoverController.popoverContentSize = CGSizeMake(320, 216);
[popoverController setDelegate:self];
//Here is your problem---------------------------------------------
newCurrencyViewController =[[newCurrencyViewController alloc]init];
//-----------------------------------------------------------------
newCurrencyViewController.delegate = self;
newCurrencyPopoverController = [[UIPopoverController alloc] initWithContentViewController:newCurrencyViewController];
newCurrencyPopoverController.popoverContentSize = CGSizeMake(320, 216);
[newCurrencyPopoverController setDelegate:self];
}
you probably want something more like
newCurrencyViewController = [[UICurrencyViewController alloc] init];
Or w/e the name of your custom view controller is

Related

Old method of handling UIPopovers not working; copied code, now getting crash

**** UPDATE** getting the following crash: [UploadViewController _viewForPresenting]: unrecognized selector sent to instance 0x7abe4c00**
The old way of using UIPopOvers was deprecated in iOS 8; so I tried to upgrade; unfortunately it's not working. I don't get the popover per se, just the top of the current view at the bottom (see image here). I'm sure something is missing here, but after spending 2 days on it, I don't see the problem. I've been away from the coding effort, so I am requesting help in solving this coding issue. This is my code (copied and modified from here):
// make the popover
UIViewController * popoverContent = [UIViewController new];
UIView* popoverView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 614, 804)];
popoverView.backgroundColor = [UIColor colorWithWhite:(CGFloat)1.0 alpha:(CGFloat)1.0]; // frame color?
popoverContent.view = popoverView;
//resize the popover view shown in the current view to the view's size
popoverContent.preferredContentSize = CGSizeMake(614, 804);
// NSString *urlAddress = #"https://google.com";
NSString *urlAddress = #"http://pragerphoneapps.com/upload-help/";
NSURL *url = [NSURL URLWithString:urlAddress];
NSURLRequest *requestObj = [NSURLRequest requestWithURL:url];
// add the UIWebView for RichText
UIWebView *webView = [[UIWebView alloc] initWithFrame: popoverView.frame];
webView.backgroundColor = [UIColor whiteColor]; // change background color here
// add the webView to the popover
[webView loadRequest:requestObj];
[popoverView addSubview:webView];
//create a popover controller
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
UIViewController *controller = [storyboard instantiateViewControllerWithIdentifier:#"UploadViewController"];
// present the controller
controller.modalPresentationStyle = UIModalPresentationPopover;
[self presentViewController: controller animated:YES completion:nil];
// configure the Popover presentation controller
UIPopoverPresentationController *popController = [controller popoverPresentationController];
popController.permittedArrowDirections = UIPopoverArrowDirectionUp;
// get warning on the following line: Incompatible pointer types assigning to 'UIBarButtonItem * _Nullable' from 'UploadViewController *'
popController.barButtonItem = self;
// also get warning on the following line: Assigning to 'id<UIPopoverPresentationControllerDelegate> _Nullable' from incompatible type 'UploadViewController *const __strong'
popController.delegate = self;
popController.sourceView = popoverView;
popController.sourceRect = CGRectMake(10, 10, 614, 804);
Read the warning you're getting:
// get warning on the following line: Incompatible pointer types assigning to 'UIBarButtonItem * _Nullable' from 'UploadViewController *'
popController.barButtonItem = self;
UIPopoverPresentationController's barButtonItem property is supposed to be a UIBarButtonItem. You're setting it to your view controller, which isn't a UIBarButtonItem. The result is that UIKit tries to send messages to your view controller that UIBarButtonItem would be able to accept, but since your view controller isn't a UIBarButtonItem, it doesn't know how to handle those messages, and you crash.
Also, fix this while you're at it:
// also get warning on the following line: Assigning to 'id<UIPopoverPresentationControllerDelegate> _Nullable' from incompatible type 'UploadViewController *const __strong'
popController.delegate = self;
This one's an easy fix; just make your UploadViewController class conform to the UIPopoverPresentationControllerDelegate protocol (and implement whatever methods you need to make it function properly as such).
Tl;dr: Don't assign objects to properties of incompatible types. Also: When the compiler gives you a warning, it's trying to help ya ;-)

UIPopover on ARC

I am using ARC on an iPad app with the code below, the popover flashes on the screen, but doesn't stay.
What I am doing wrong?
Please help
- (IBAction)photoLibraryAction:(id)sender
{
UIImagePickerController *imagePicker = [[UIImagePickerController alloc] init];
[imagePicker setDelegate:self];
UIPopoverController *pop1 = [[UIPopoverController alloc] initWithContentViewController:imagePicker];
[pop1 setDelegate:self];
[pop1 presentPopoverFromBarButtonItem:sender permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
[pop1 setPopoverContentSize:CGSizeMake(320, 400)];
}
if ([pop1 isPopoverVisible])
{
// Popover is not visible
[pop1 dismissPopoverAnimated:YES];
}
}
In ARC, pop1 will be released right after -photoLibraryAction: returns, because ARC doesn't know that -presentPopoverFromBarButtonItem:permittedArrowDirections: makes the object usable beyond its scope.
You'll have to add an instance variable for your popover controller so ARC doesn't release it. Your if-statement is invalid, too, because when the method returns, pop1 is no longer available for you to use. You'll have to use an instance variable there as well.

UINavigationController for table inside of UITabController

my app is built with a UITabController and works as imagined. However, for one of the views within my UITabBar, I would like to add a table that when something is pressed will take me somewhere. And I would like to do this just within this one view.
I know how to build a table and populate and get it to go somewhere but my issue is I can't seem to get my app to run with the table. I feel like my connections are off and specifically with appDelegates. I already had two appDelegate files (.h & .m) before adding the UINavigationController so from here I really don't know what to do. I took apple's simpleTableView tutorial files and copied them over to mine. It still crashes. I even copied there appDelegate files (so now I have 4) but the same deal. This is the error I am getting but in general I just feel lost with the delegates and connections.
2011-12-12 12:08:50.302 TabbedCalculation[68713:207] * Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[ setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key delegate.'
* Call stack at first throw:
If anyone can offer any help, it would be much appreciated.
Thanks!
P.S. I have changed within the mainWindow.xib of the UITabController to point one of the tabs to the appropriate class and xib file so that is not the issue but I have noticed that many tutorials want within the app delegate this line to the navController:
[window addSubview:[navigationController view]];
but I have already set it to the tabBarController.
Your app can only use one set of app delegate files. So copying over a the example's app delegate files does not mean they are being utilized. You need a navigation controller inside the specific tab you want to contain the tableview. Here is an example of a navigation controller inside a tab bar controller, by modifying didFinishLaunchingWithOptions in the app delegate:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];
// Override point for customization after application launch.
AllTaskViewController *view1 = [[AllTaskViewController alloc] initWithNibName:#"AllTaskView" bundle:nil];
view1.title = #"All Tasks";
TodayTaskViewController *view2 = [[TodayTaskViewController alloc] initWithNibName:#"TodayTaskView" bundle:nil];
view2.title = #"Today's Tasks";
HistoryViewController *view3 = [[HistoryViewController alloc] initWithNibName:#"HistoryView" bundle:nil];
view3.title = #"History";
SettingsTableViewController *view4 = [[SettingsTableViewController alloc] initWithNibName:#"SettingsTableView" bundle:nil];
view4.title = #"Settings";
UINavigationController *nav1 = [[UINavigationController alloc] initWithRootViewController:view1];
UINavigationController *nav2 = [[UINavigationController alloc] initWithRootViewController:view2];
UINavigationController *nav3 = [[UINavigationController alloc] initWithRootViewController:view3];
UINavigationController *nav4 = [[UINavigationController alloc] initWithRootViewController:view4];
[view1 release];
[view2 release];
[view3 release];
[view4 release];
self.tabBarController = [[[UITabBarController alloc] init] autorelease];
self.tabBarController.viewControllers = [NSArray arrayWithObjects:nav1, nav2, nav3, nav4, nil];
[nav1 release];
[nav2 release];
[nav3 release];
[nav4 release];
self.window.rootViewController = self.tabBarController;
[self.window makeKeyAndVisible];
return YES;
}
Note: All 4 view controllers have an individual navigation controller, and each one is a custom view controller. I used nibs here, but you don't necessarily have to. And the app itself has no main window, you would need to modify this slightly if you are using a main window for the app.

UITabbarcontroller in IOS5 throws UIViewControllerHierarchyInconsistency exception

I have the following code for the UITabbarcontroller:
NSMutableArray *arr = [[NSMutableArray alloc] init];
tabBarController = [[UITabBarController alloc] init];
FirstViewController *firstview = [[FirstViewController alloc] init];
[tabBarControllerViews addObject:firstview];
[firstview release];
SecondViewController *secondview = [[SecondViewController alloc] init];
[tabBarControllerViews addObject:secondview];
[secondview release];
[tabBarController setViewControllers:arr animated:YES];
[arr release];
self.view = tabBarController.view;
This code runs fine on IOS4. I tried it on IOS5 beta and get the following error when tapping on a UITabbarItem:
*** Terminating app due to uncaught exception 'UIViewControllerHierarchyInconsistency',
reason: 'child view controller:<FirstViewController: 0x6e03be0> should have parent view
controller:<MainViewController: 0x6816d20> but actual parent is:<UITabBarController: 0x6b0c110>'
replace:
self.view = tabBarController.view;
with:
[self.view addSubview:tabBarController.view];
This will also be backwards compatible with IOS3&4.
Had the same pattern (and problem) in my code. Joe's solution didn't work for me. Looking at the snippet, I'm guessing that you derive a class from UIViewController to allow you to customize something.
The thing to do here, and it is quite simple, is to derive from UITabBarController rather than UIViewController, don't create tabBarController, and anywhere you reference tabBarController, substitute self.
5 minutes and you're no longer throwing the inconsistency exception and you remain backwards compatible with iOS 4. You can still do all of your customization in your derived class (monkeying with the nav controller, etc).
If you have built a complex derivation of UIViewController you need to use, this could be more work.
One small gotcha - if you override LoadView, you'll find that it gets called during the init for the UITabBarController. Makes it hard to set members prior to LoadView, so you may need to split up your initialization.
Good luck!
You cannot push or present a UITabbarViewController. Is your First View Controller a UITabBarController ?
I struggled with the same problem.
When you create a new Master-Detail Application(without story board), you can see this codes below from AppDelegate.m.
MasterViewController *masterViewController = [[MasterViewController alloc] initWithNibName:#"MasterViewController" bundle:nil];
self.navigationController = [[UINavigationController alloc] initWithRootViewController:masterViewController];
self.window.rootViewController = self.navigationController;
[self.window makeKeyAndVisible];
"BE NOT DEPENDENT ON MainWindow"
Just start from your own ViewController and set it to delegate.
And don't forget to unlink view from MainWindow.xib else the view will called 2 times.

UINavigationController: Simplest Example

I'm trying to do very simple example of a UINavigationController. Here is my code:
- (void)viewDidLoad {
[super viewDidLoad];
This next line works, or at least doesn't blow up.
navController = [[UINavigationController alloc] initWithRootViewController:self];
self.title = #"blah";
PageOneController *one = [[[PageOneController alloc]init] autorelease];
Example 1. THIS LINE DOES NOTHING
[navController pushViewController:one animated:NO];
Example 2. THIS LINE WORKS (but no nav controller, of course)
[self.view addSubview:one.view];
}
Why am I unable to push ViewController instances onto the navController and see the screen change?
Note: I realize that I might have my concepts backwards and I don't need to have my view referencing a UINavigationController... or something.
- (void)viewDidLoad {
[super viewDidLoad];
PageOneController *one = [[[PageOneController alloc]init] autorelease];
one.title = #"blah";
navController = [[UINavigationController alloc] initWithRootViewController:one];
[self.view addSubview:navController.view];
}
The basic idea behind it is that a navigation controller's root view controller is the controller which view will be displayed first in the navigation controller hierarchy. The root controller is not the view controller that you plug the navigation controller into. Hope this helps.
I'm just restating #E-ploko's answer, which is 100% correct (which is why I marked it best answer).
You need more views (and view controllers) to use the UINavigationController. One of them houses the UINavigationController, and its rootViewController is the first page of the series (the one that has no "back").
I got rid of the external dependencies for the code sample: obviously this is monolithic sample code, not monolithic real code.
- (void)viewDidLoad {
[super viewDidLoad];
UIViewController *one = [[UIViewController alloc] init];
[one.view setBackgroundColor:[UIColor yellowColor]];
[one setTitle:#"One"];
navController = [[UINavigationController alloc] initWithRootViewController:one];
// here 's the key to the whole thing: we're adding the navController's view to the
// self.view, NOT the one.view! So one would be the home page of the app (or something)
[self.view addSubview:navController.view];
// one gets reassigned. Not my clearest example ;)
one = [[UIViewController alloc] init];
[one.view setBackgroundColor:[UIColor blueColor]];
[one setTitle:#"Two"];
// subsequent views get pushed, pulled, prodded, etc.
[navController pushViewController:one animated:YES];
}