UISplitViewController - detailView is not showing - ios

In AppDelegate we create an DetailView, then a loginView is presented (UIModalPresentationFullScreen) on top of it. After logged in, loginView is dismissed.
DetailView is having a tableView and when you select a cell/row and 2nd detailView is pushed.
What I did so far:
In AppDelegate I ask for UI_USER_INTERFACE_IDIOM() and when idiom is iPad i create a splitView:
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
{
FirstDetailViewController* fdvc = [[FirstDetailViewController alloc] initWithNibName:#"FirstDetailViewController" bundle:nil];
SecondDetailViewController* sdvc = [[SecondDetailViewController alloc] initWithNibName:#"SecondDetailViewController" bundle:nil];
UINavigationController* fdvcNavigationController = [[UINavigationController alloc] initWithRootViewController:fdvc];
UINavigationController* sdvcNavigationController = [[UINavigationController alloc] initWithRootViewController:sdvc];
splitViewController = [[UISplitViewController alloc] init];
splitViewController.viewControllers = [NSArray arrayWithObjects:fdvcNavigationController, sdvcNavigationController, nil];
After login my LoginView is dismissed and my UISplitViewController is shown, with the 1st DetailView on the left side (master). So everything went fine here.
Now I go to the FirstDetailViewController.m and search for the didSelectRowAtIndexPath because there I find the pushView to SecondDetailViewController in the "iPhone version".
And here is where i am stuck. I have tried several SplitView tutorials and read problems of others regarding splitview.
But I think my problem is some kind of "basic" because I am new to programming / iOS in general and don't know all my tools.
Any help would be appreciated.

When writing apps with a table view and some other sort of "display" view for items in the table, I do this so it works on both devices:
// In App Delegate...
-(BOOL) application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Create views
MyTableViewController* myTable = [[MyTableViewController alloc] init];
MyDetailViewController* myDetail = [[MyDetailViewController alloc] init];
UINavigationController* tableNav = [[UINavigationController alloc] initWithRootViewController:myTable];
UINavigationController* detailNav = [[UINavigationController alloc] initWithRootViewController:myDetail];
// Check device type
if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) {
// Use a split view
UISplitViewController* splitView = [[UISplitViewController alloc] init];
split.viewControllers = #[tableNav, detailNav];
self.window.rootViewController = split;
} else {
// Use a single view for iPhone
self.window.rootViewController = tableNav;
}
}
.
// In table view
-(void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// Check device type
if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) {
// Tell the already visible detail view to load something
MyData* data = ...; \\ Get your thing to display however you want
[[NSNotificationCenter defaultCenter] postNotificationName:#"DisplayMyData" object:data];
} else {
// No detail controller exists yet, so create it
MyDetailViewController* myDetail = [[MyDetailViewController alloc] init];
[self.navigationController pushViewController:myDetail animated:YES];
[[NSNotificationCenter defaultCenter] postNotificationName:#"DisplayMyData" object:data];
}
}
.
// In detail view
-(void) viewDidLoad {
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(displayData:) name:#"DisplayMyData" object:nil];
}
-(void) displayData:(NSNotification*)notify {
MyData* data = (MyData*) notify.object;
... // Display however...
}

Related

Replacing Detail View Controller

I am using a split view controller and have an issue when replacing my detail view controller with a new view controller. When I start the app I get my empty view controller as my detail view controller and hit the button on the navigation bar to show the master view controller. When I select a row in master view controller my detail view controller is replaced with the appropriate controller. It works fine the first time you select a row, but every time after that, when a row is selected from the master controller, the master controller doesn't go away so it just stays on top of the detail controller.
AppDelegate:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Override point for customization after application launch.
MasterViewController *masterVC = [[MasterViewController alloc]initWithNibName:#"MasterViewController" bundle:nil];
UINavigationController *masterNavController = [[UINavigationController alloc]initWithRootViewController:masterVC];
DetailViewController *detailVC = [[DetailViewController alloc]initWithNibName:#"DetailViewController" bundle:nil];
UINavigationController *detailNavController = [[UINavigationController alloc]initWithRootViewController:detailVC];
masterVC.detailViewController = detailVC;
self.splitViewController = [[UISplitViewController alloc]init];
self.splitViewController.delegate = detailVC;
self.splitViewController.viewControllers = #[masterNavController, detailNavController];
self.window.rootViewController = self.splitViewController;
[self.window makeKeyAndVisible];
return YES;
}
MasterViewController:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
ActionAlertsViewController *rootView = [[ActionAlertsViewController alloc]initWithNibName:nil bundle:nil];
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication]delegate];
NSMutableArray *details = [self.splitViewController.viewControllers mutableCopy];
if (indexPath.section == 0) {
if (indexPath.row == 0) {
rootView.title = #"Action Alerts";
rootView.background = [UIImage imageNamed:#"capitol"];
rootView.urlString = #"http://kyfbnewsroom.com/category/public-affairs/notifications/feed/";
[rootView fetchEntries];
UINavigationController *detailNav = [[UINavigationController alloc]initWithRootViewController:rootView];
if (details.count > 1) {
[details replaceObjectAtIndex:1 withObject:detailNav];
} else {
[details insertObject:detailNav atIndex:1];
}
appDelegate.splitViewController.viewControllers = details;
appDelegate.window.rootViewController = self.splitViewController;
appDelegate.splitViewController.delegate = rootView;
[appDelegate.splitViewController viewWillAppear:YES];
}
}
}
I don't know where to start:
Don't copy/clone UIViewControllers: [self.splitViewController.viewControllers mutableCopy]
Don't invoke system-initiated messages: [appDelegate.splitViewController viewWillAppear:YES]
Don't reset the root view controller" appDelegate.window.rootViewController
Don't go back to the AppDelegate to find out which view you are
#beyowulf noted: You shouldn't be calling viewWillAppear that is a view controller lifecycle method that is called by the system. You should say something like [self.splitViewController showDetailViewController:detailNav sender:self]
Do read the documentation and follow the tutorials:
Raywenderlich
NSHipster

No back button on UINavigationBar inside UITabbarController

I am working on an app for iOS6 and iOS7. I just found a weird behavior that varies between the two versions.
My app has a UITabbarController with 3 tabs and each tab as UINavigationController in it.
On iOS6 when I do a push on any of these navControllers the 2nd ViewController after the root does not show a back button in the navigationBar. However any subsequent pushes show the back button.
On iOS7, however, I see a back button with no title after 1st push and a back button with title "Back" after subsequent push.
Is this behavior normal? I can't understand why this is happening. How do I make sure I have a back button after every push irrespective of the OS version.
EDIT
Here is some code for clarity :
This is how I am initiating the UITabBarController
- (id)init {
self = [super init];
if (self) {
NSMutableArray *vcs = #[].mutableCopy;
JBNavigationVC *navVC = nil;
if ([JBUser sharedInstance].isLoggedIn) {
_productCategoryVC = [[JBProductCategoryVC alloc] init];
_productCategoryVC.title = #"Products";
navVC = [[JBNavigationVC alloc] initWithRootViewController:_productCategoryVC];
[vcs addObject:navVC];
_triviaVC = [[JBTriviaVC alloc] init];
_triviaVC.title = #"Trivia";
navVC = [[JBNavigationVC alloc] initWithRootViewController:_triviaVC];
[vcs addObject:navVC];
_infoVC = [[JBInfoVC alloc] init];
_infoVC.title = #"Info";
navVC = [[JBNavigationVC alloc] initWithRootViewController:_infoVC];
[vcs addObject:navVC];
}
self.viewControllers = vcs;
}
return self;
}
JBNavigationVC is a subclass of UINavigationController. This is how I transition to the tabbarVC in AppDelegate
_tabBarVC = [[JBTabBarVC alloc] init];
[_tabBarVC setSelectedIndex:0];
[self.window setRootViewController:_tabBarVC];
This sets the first VC to front. This VC is has a UITableView in it and in - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath I do this:
if (!_productsListVC) {
_productsListVC = [[JBProductsListVC alloc] init];
}
_productsListVC.category = category;
[self.navigationController pushViewController:_productsListVC animated:YES];

iOS selected tab

I'm trying to determine which tab has been selected by the user. I melded this together from a couple of tutorials on iOS tab bars. In my appDelegate I have this code:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
//We need to implement the view controllers within the tab controller and make the tab controller the root controller of our app - note we are only using view 1-3 at first.
FirstViewController *fistView = [[FirstViewController alloc] initWithNibName:#"FirstViewController" bundle:nil];
SecondViewController *secondView = [[SecondViewController alloc] initWithNibName:#"SecondViewController" bundle:nil];
ThirdViewController *thirdView = [[ThirdViewController alloc] initWithNibName:#"ThirdViewController" bundle:nil];
FourthViewController *fourthView = [[FourthViewController alloc] initWithNibName:#"FourthViewController" bundle:nil];
NSArray *viewControllersArray = [[NSArray alloc] initWithObjects:fistView, secondView, thirdView, fourthView, nil];
self.tabController = [[UITabBarController alloc] init];
[self.tabController setViewControllers:viewControllersArray animated:YES];
self.window.rootViewController = self.tabController;
//end custom code
self.window.backgroundColor = [UIColor whiteColor];
[self.window makeKeyAndVisible];
return YES;
}
Is viewControllerArray the delegate for my tabController?
When I place this code on the page nothing happens:
- (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController {
if (tabBarController.selectedIndex == 0) {
NSLog(#"ok");
}
}
In this case, your app delegate should be the delegate for the tabBarController.
You can simply add self.tabController.delegate = self and make sure that your AppDelegate conforms to the UITabBarControllerDelegate protocol.
I also suggest placing a log outside the if in your delegate method, to confirm that it is actually called.

Issue Presenting View modally after a UISplitView loads

I'm new to UISplitView development, so I'm sure there is something obvious I'm doing wrong. I have a basic UISplitView iPad app that loads up with two UITableView controllers when the app launches. This works just fine.
What I am trying to do is immediately upon launch, presenting an "authentication" view modally so that a user will need to login before continuing. Here is the code I have so far which compiles and works without breaking, but the view is not showing.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
MasterViewController *masterViewController = [[MasterViewController alloc] initWithNibName:#"MasterViewController" bundle:nil];
UINavigationController *masterNavigationController = [[UINavigationController alloc] initWithRootViewController:masterViewController];
DetailViewController *detailViewController = [[DetailViewController alloc] initWithNibName:#"DetailViewController" bundle:nil];
UINavigationController *detailNavigationController = [[UINavigationController alloc] initWithRootViewController:detailViewController];
self.splitViewController = [[UISplitViewController alloc] init];
self.splitViewController.delegate = detailViewController;
self.splitViewController.viewControllers = [NSArray arrayWithObjects:masterNavigationController, detailNavigationController, nil];
masterViewController.detailViewController = detailViewController;
masterViewController.managedObjectContext = self.managedObjectContext;
self.window.rootViewController = self.splitViewController;
[self presentAuthenticate];
[self.window makeKeyAndVisible];
applicationDidLaunch = YES;
return applicationDidLaunch;
}
- (void) presentAuthenticate {
AuthenticateViewController *loginController = [[AuthenticateViewController alloc] initWithNibName:#"AuthenticateViewController" bundle:nil];
[loginController setModalTransitionStyle:UIModalTransitionStyleCoverVertical];
[loginController setModalPresentationStyle:UIModalPresentationFormSheet];
if ([self.splitViewController respondsToSelector:#selector(presentViewController:animated:completion:)]) {
[self.splitViewController presentViewController:loginController animated:NO completion:nil];
} else {
[self.splitViewController presentModalViewController:loginController animated:NO]; //iOS 4 works fine with or without animation
}
}
I defined the AuthenticateViewController as a View with a few textfields in it and have it wired to the File's Owners view.
Thanks ahead of time!
A viewcontroller will not allow to push/present on anotherview unless and until the view is complete loading.
Simple saying we are not allow to call presentModalViewController/pushViewController in a viewcontroller viewDidLoad/viewWillAppear. we need to call this in viewDidAppear.
I had the same issue you said.
Some Solution I can say are,
Do the loading of AuthenticateViewController after [self.window makeKeyAndVisible]; and in a performSelctor (may be with a delay).
Move the code to display AuthenticateViewController in SplitView's DetailView controller viewDidAppear.
thanks,
Naveen Shan

Orientation Problem while using insertSubview

I get an orientation problem while using the following to code to display a view on top of a split view.
[window addSubview:aSplitViewController.view];
[window insertSubview:aViewController.view aboveSubview:aSplitViewController.view];
the plain view has a couple of buttons and labels.
So the problem I am facing is that the first view opens in landscape mode but the labels and buttons on the view are in portrait mode.
UPDATE: Here is some code so if anyone wants to see more details...
In my App Delegate
- (void) makeSplitViewController {
NSMutableArray *controllers = [NSMutableArray arrayWithArray:tabBarController.viewControllers];
// First tabbbar item
// detail view
detailViewController = [[DetailViewController alloc] initWithNibName:#"DetailView" bundle:nil];
UINavigationController *navDetailView = [[[UINavigationController alloc] initWithRootViewController:detailViewController] autorelease];
navDetailView.hidesBottomBarWhenPushed = YES;
// root view
rootViewController = [[RootViewController alloc] initWithStyle:UITableViewStylePlain];
rootViewController.detailViewController = detailViewController;
rootViewController.navigationItem.title = #"List";
UINavigationController *navRootView = [[[UINavigationController alloc] initWithRootViewController:rootViewController] autorelease];
navRootView.hidesBottomBarWhenPushed = YES;
navRootView.navigationBar.barStyle = UIBarStyleBlackTranslucent;
splitViewController = [[UISplitViewController alloc] init];
splitViewController.tabBarItem.title = #"Face Sheet";
splitViewController.tabBarItem.image = [UIImage imageNamed:#"gear1.png"];
splitViewController.navigationItem.title = #"Face Sheet";
splitViewController.viewControllers = [NSArray arrayWithObjects:navRootView, navDetailView, nil];
splitViewController.delegate = detailViewController;
splitViewController.hidesBottomBarWhenPushed = YES;
[controllers addObject:splitViewController];
// Second tabbbar item
scoreViewController = [[ScoreCardViewController alloc] initWithNibName:#"TableViewController" bundle:nil];
scoreViewController.tabBarItem.title = #"Score Card";
scoreViewController.tabBarItem.image = [UIImage imageNamed:#"gear1.png"];
scoreViewController.navigationItem.title = #"Score Card";
[controllers addObject:scoreViewController];
tabBarController.viewControllers = controllers;
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
// Create tabbar
tabBarController = [[UITabBarController alloc] init];
//tabBarController.delegate = self;
// Set window
[window addSubview:splashController.view];
[window insertSubview:tabBarController.view belowSubview:splashController.view];
[self.window makeKeyAndVisible];
application.statusBarOrientation = UIInterfaceOrientationLandscapeRight;
return YES;
}
and here is the code in my SplashScreenView
- (IBAction) proceedButtonClick:(id)sender
{
// Initialize loginpopview
PhysicianLoginViewController *loginViewController = [[PhysicianLoginViewController alloc] init];
popOverController = [[UIPopoverController alloc] initWithContentViewController:loginViewController];
popOverController.popoverContentSize = CGSizeMake(350, 200);
popOverController.delegate = self;
// Set a notification to dismiss it later
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(loginViewControllerDone:) name:#"loginViewControllerDone" object:popOverController.contentViewController];
// Present popover
if ([popOverController isPopoverVisible])
{
[popOverController dismissPopoverAnimated:YES];
}
else
{
[popOverController presentPopoverFromRect:CGRectMake(485, 600, 100, 100) inView:self.view permittedArrowDirections:UIPopoverArrowDirectionDown animated:YES];
}
}
// Dismiss popview controller and setup the tabbar
- (void)loginViewControllerDone:(NSNotification *)notification{
[[NSNotificationCenter defaultCenter] removeObserver:self];
// Button in content view controller was tapped, dismiss popover...
[self.popOverController dismissPopoverAnimated:YES];
// remove subview
[self.view removeFromSuperview];
// set tabbar
i3EAppDelegate *appDelegate = (i3EAppDelegate *) [[UIApplication sharedApplication]delegate];
[appDelegate makeSplitViewController];
}
It would be great if someone could point out where I am going wrong. I have been stuck with this problem for quite a few days and I have tried everything that comes to my mind...
UIWindow has a subview that it uses for rotations and puts other views inside of that. You need to insert yourself into the root view (or something lower), not the window. Look at -[UIWindow rootViewController].
UIView *rootView = [[[self window] rootViewController] view];
[rootView addSubview:view];
This will work as long as you're using something with a root view controller. This will work as long as rootViewController isn't nil. If you're doing a raw "View Based" application, then it's usually best to pick another view and add your view as its sibling rather than digging through the undocumented hierarchy:
UIView *sibling = ... (some other view)
[[sibling superview] addSubview:view];

Resources