Presenting view controllers on detached ... - sometimes - objective-c

Like others coming to iOS8, I'm getting the warning:
Presenting view controllers on detached view controllers is discouraged <EditItineraryViewController: 0x7ca56e00>.
This is caused by the following code:
- (void)editActivityDetailsForIndexPath:(NSIndexPath *)indexPath {
NSPredicate *predicateForDisplay = [[NSPredicate alloc] init];
switch (indexPath.section) {
case kIncompleteActivitiesSection:
predicateForDisplay = _predIncomplete;
break;
case kCompleteActivitiesSection:
predicateForDisplay = _predComplete;
break;
default:
break;
}
NSString *theActivityName = [[NSString alloc] init];
theActivityName = [[[_activitiesArray filteredArrayUsingPredicate:predicateForDisplay] objectAtIndex:indexPath.row] activityName];
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
ActivityDetailViewController *activityDetailVC = [storyboard instantiateViewControllerWithIdentifier:#"ActivityDetailView"];
activityDetailVC.modalPresentationStyle = UIModalPresentationFormSheet;
activityDetailVC.delegate = self;
// send the activity to the view
activityDetailVC.theActivity = [[_activitiesArray filteredArrayUsingPredicate:predicateForDisplay] objectAtIndex:indexPath.row];
// configure the look of the view
_activityDetailsPopover = [[UIPopoverController alloc] initWithContentViewController:activityDetailVC];
_activityDetailsPopover.delegate = self;
ItineraryCell *cell = (ItineraryCell *)[self.itineraryTableView cellForRowAtIndexPath:indexPath];
activityDetailVC.contentView.backgroundColor = [UIColor whiteColor];
activityDetailVC.navigationBar.barTintColor = _colorSchemeLightColor;
activityDetailVC.navigationBar.titleTextAttributes = #{NSForegroundColorAttributeName:_colorSchemeColor};
activityDetailVC.saveButton.tintColor = _colorSchemeColor;
activityDetailVC.cancelButton.tintColor = _colorSchemeColor;
activityDetailVC.labelB.textColor = _colorSchemeColor;
activityDetailVC.labelM.textColor = _colorSchemeColor;
activityDetailVC.activityTitle.textColor = _colorSchemeColor;
activityDetailVC.activityTitle.font = [UIFont boldSystemFontOfSize:17.0];
// present the view
[_activityDetailsPopover presentPopoverFromRect:cell.cellDetailsButton.frame inView:cell.cellDetailsButton.superview permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
}
This is an iPad app and in this case, the popover is being presented in such a way as to have its little pointer pointing at an "i" icon that sits on a tableview cell in a tableview.
This works in three other places in my app without causing the warning at all. But for some reason, with this implementation, it's causing the warning. The weird thing is, this was the first place in my app I used this means of presenting the popover from a tableview cell and the other instances are just copies of this code!
Any ideas what I can look at to figure out where the hierarchy is buggered up? Is it related to how the tableview cell is being generated and then this popover presented on top of it, or does it have to do strictly with the popover itself? Or could it have to do with the tableview. I don't even know where to start to look.
Thanks so much!

I worked around this problem by presenting the popover from the view controller that presents the tableView (in my case a collection view). The problem arises when you present from a cell.
I'm assuming any cell is considered a "detached view controller" so presenting from them will give you this error and will not receive rotation events, which in turn will not use the popoverPresentationController:willRepositionPopoverToRect:inView: callback.

Related

Can I Show A SplitViewController Using presentModalViewController?

I don't know if what I'm trying to do is possible, but because I haven't the desired results, I guess not.
What I'm trying and need to do is to call a SplitViewController from a previous ViewController, using presentViewController.
I know, SplitViewController have to be the rootViewController, but I need to explore the most possible options to achieve what I need to do.
I have a MainMenu with buttons, and with every button, I need to call a SplitViewController. First, how can do this?
What I'm trying to do is this:
First, in AppDelegate I'm calling the MainMenu, and add as a subview and other things:
-(BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[window addSubview:self.mainMenu.view];
[self.mainMenu presentModalViewController:self.firstMenu animated:NO];
[self.window makeKeyAndVisible];
return YES;
}
Then, in the MainMenu, I'm calling SecondViewController, in modal view, using this code:
SecondViewController *secV = [[SecondViewController alloc]initWithNibName:#"SecondViewController" bundle:nil];
secV.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentModalViewController:secV animated:YES];
In this SecondViewController, I'm creating SplitViewController, with Master & DetailViewController's, using this code:
-(void)viewDidLoad{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
UISplitViewController *splitViewController = [[UISplitViewController alloc]init];
SecondMenuViewController *secMenu = [[SecondMenuViewController alloc]init];
UINavigationController *navLef = [[UINavigationController alloc]init];
[navLef pushViewController:secMenu animated:NO];
SecondMainViewController *secMain = [[SecondMainViewController alloc]init];
UINavigationController *navRig = [[UINavigationController alloc]init];
[navRig pushViewController:secMain animated:NO];
splitViewController.delegate = secMain;
splitViewController.viewControllers = [NSArray arrayWithObjects:navLef, navRig, nil];
MainAppDelegate *mainApp = [[MainAppDelegate alloc]init];
[mainApp changeRootViewController:splitViewController];
navRig = nil;
navLef = nil;
secMain = nil;
secMenu = nil;
splitViewController = nil;
}
As you can see, I'm calling a method in MainAppDelegate, to change view and RootViewController, because SplitViewController have to be RootViewController. This is the method:
-(void)changeRootViewController:(UISplitViewController *)splitViewController{
[self.window addSubview:splitViewController.view];
self.window.rootViewController = splitViewController;
}
I know, this looks like a mess. And when I run, the SplitViewController never shows, so I assume, what I'm trying to do is not possible? Or In what I'm wrong?
If it is everything, what can I do to show a SplitViewController after my MainViewController?
I'm using XCode4.4 and iOS5
Thank you very much
A better way would be to make your UISplitViewController the root view controller in application:didFinishLaunchingWithOptions:. Then present your MainMenu on top of it. You can change the subviews displayed by the split view controller to correspond to what button the user pushes in your MainMenu.
First, didFinishLaunchingWithOptions: is too early to be calling presentModalViewController. You haven't even got an interface yet!
Second, you don't seem to have a root view controller (although perhaps you're getting one from a nib? you should probably stop doing that; use the techniques shown in the current application templates).
Third, note that now that we have custom container views, there is no need for you to use UISplitViewController at all; you can construct your own view / view controller hierarchy, and you might be happier doing so, since UISplitViewController is not a very well-constructed class.

Two UIPopovers in One View

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

Trouble with matchmaking in iOS with gamekit and game centre

At the moment I'm trying to add online multiplayer to my iOS game (that uses UIKit) using gameKit/GameCenter for matchmaking. I'm trying to present game center's matchmaker interface but with varied success. I have two view controllers (with respective .xib files), mainMenuViewController and onlineViewController, along with a file called GCHelper.h.
In CGHelper I have this:
- (void)findMatchWithMinPlayers:(int)minPlayers
maxPlayers:(int)maxPlayers
viewController:(UIViewController *)viewController
delegate:(id<GCHelperDelegate>)theDelegate
{
if (!gameCenterAvailable) return;
self.presentingViewController = viewController;
delegate = theDelegate;
[presentingViewController dismissModalViewControllerAnimated:NO];
GKMatchRequest *request = [[GKMatchRequest alloc] init];
request.minPlayers = minPlayers;
request.maxPlayers = maxPlayers;
GKMatchmakerViewController *mmvc = [[GKMatchmakerViewController alloc] initWithMatchRequest:request];
mmvc.matchmakerDelegate = self;
[presentingViewController presentModalViewController:mmvc animated:YES];
}
I use this function in my views to show the matchmaking interface. At first I tried it in mainMenuViewController, which is my first view controller using this:
[[GCHelper sharedInstance] findMatchWithMinPlayers:2 maxPlayers:2 viewController:self delegate:self];
And that worked fine but that's not where I wanted to do it, so I tried the exact same thing from onlineViewController and nothing happens, no error, just the blank view of onlineViewController.view stares back at me. I'm fairly new at this so I'm wondering if it's down to how I'm dealing with the views, here's the code I use to switch from the mainMenu to the other view:
-(IBAction)showOnlineView
{
OnlineViewController *onlineViewController = [[OnlineViewController alloc] initWithNibName:#"OnlineViewController" bundle:nil];
UIView *currentView = self.view;
UIView *theWindow = [currentView superview];
[currentView removeFromSuperview];
[theWindow addSubview:onlineViewController.view];
}
Any help would be greatly appreciated. Also, I'm not sure how I should deal with going back to the main menu if the user presses cancel (although that may become self evident once I've got this working, I just thought I'd mention it in case this lack of knowledge on my part makes it obvious I'm going about the whole thing incorrectly).

Create a UINavigationBar without using [Projectname]AppDelegate?

I'm trying to create another UINavigationBar in my project, but it seems that I'm missing some key detail. When the application first loads, it does have it's own navigation system, but now I'm trying to add another navigation to a modal.
Many tutorials show you need to connect the view to the [self window], which only seems to work in the AppDelegate files, but when I've tried placing the code* in viewDidLoad, I can never seem to build without any errors.
I've seen this in multiple apps, but how is this done (programmatically or with IBuilder)?
Thanks!
Example code I've tried in viewDidLoad
UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:[self viewController]];
[self.window addSubview:navigationController.view];
You don't show enough code to be able to understand how you do it, but it seems to me that you are showing a controller modally, then trying to add as a subview to its view a navigation controller.
You can try and directly push modally your navigation controller (from your app delegate or where it makes sense for your app):
(IBAction) navigateToSecondaryViewController {
if (secondaryViewController == nil) {
informationTableViewController = [[SecondaryViewController alloc]
initWithNibName:#"SecondaryViewController"
bundle:[NSBundle mainBundle]];
secondaryViewController.delegate = self;
}
if (navController == nil) {
navController = [[UINavigationController alloc]
initWithRootViewController:secondaryViewController];
}
[self presentModalViewController:navController animated:YES];
}
Full example here.

Add button to my View "Header/Title" bar

In my AppDelegate, I create a UITabBarController and UINavigationController dynamically. I then add 3 views to it.
// Prepare the tab bar controller
tabBarController = [[UITabBarController alloc] init];
// Switch controller
UserSettingsController *settingsController = [[UserSettingsController alloc] init];
// Switches controller
SwitchesController *switchesController = [[SwitchesController alloc] init];
// Help controller
HelpController *helpController = [[HelpController alloc] init];
NSArray *controllers = [NSArray arrayWithObjects: switchesController, settingsController, helpController, nil];
tabBarController.viewControllers = controllers;
if (self.navigationController == nil) {
self.navigationController = [[UINavigationController alloc] initWithRootViewController:tabBarController];
}
[window addSubview:navigationController.view];
When I initially did this, I noticed that I now have a header/title bar at the top of my page. It's blank and really just takes up space. I'd like to utilize it though and add a "refresh" button to one of my views.
In the view I'm interested in, I attempted the following:
-(void)viewDidLoad {
[super viewDidLoad];
UIBarButtonItem *refresh = [[UIBarButtonItem alloc] initWithTitle:#"Refresh" style:UIBarButtonItemStylePlain target:self action:#selector(refreshSwitches:)];
self.navigationItem.rightBarButtonItem = refresh;
[refresh release];
}
-(void)refresh{
...
}
No button showed up, so I'm either way off or I'm missing something.
Any suggestions?
Thanks
I believe the problem is that your view controllers that you've added to the tabbar controller are not encapsulated by navigation controllers. Although your UITabBarController has a navigation controller, this does not implicitly give all of its tabs navigation controllers (and without a nav controller, a view controller's navigationItem won't do anything.) To remedy this problem I'd suggest encapsulating your view controllers with navigation controller upon initialization:
ex:
UserSettingsController *settingsController = [[UserSettingsController alloc] init];
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:settingsController];
[settingsController release];
// etc...
NSArray *controllers = [NSArray arrayWithObjects:navController, ..., ..., nil];
tabBarController.viewControllers = controllers;
// Release your nav controllers, other cleanup
EDIT
Okay, I think I have a better idea of your setup now. I was able to get the following to work:
self.tabBarController.navigationItem.rightBarButtonItem = myButton;
An important caveat of this is that the button will persist on all of your tabbarcontroller's views unless you explicitly remove it. This may prove annoying/inelegant to maintain, but I am not sure of another solution if the view you want to have the button is not directly associated to a nav controller itself.