How to swap out masterviews in a UISplitviewcontroller? - ios

Basically, I have two MasterViewControllers for a splitViewController,but I am only able to run the app with one of them at a time. Both MasterViewControllers are using the same DetailViewController.
When the user push a tabbarbutton, the second masterViewController is pushed.
The splitViewController is straight forward declared like this in the AppDelegate
MasterViewControllerOne *mvc1 = [[MasterViewControllerOne alloc] initWithNibName:#"MasterViewControllerOne_iPad" bundle:nil];
UINavigationController *masterOneNavigationController = [[UINavigationController alloc] initWithRootViewController:mvc1];
DetailViewController *dvc = [[DetailViewController alloc] initWithNibName:#"DetailViewController_iPad" bundle:nil];
UINavigationController *detailNavigationController = [[UINavigationController alloc] initWithRootViewController:dvc];
self.splitViewController = [[UISplitViewController alloc] init];
self.splitViewController.delegate = detailViewController;
self.splitViewController.viewControllers = #[masterOneNavigationController, detailNavigationController];
self.window.rootViewController = self.splitViewController;
During runtime, I want MasterViewControllerTwo to swap with MasterViewControllerOne
Action to push the new view controller:
MasterViewControllerTwo*mvc2=[[MasterViewControllerTwo alloc]initWithNibName:#"MasterViewControllerTwo_iPad" bundle:nil];
[self.navigationController pushViewController:mvc2 animated:YES];
This swaps in the mvc2 on top of mvc1. However, tapping the rows of the tableview in mvc2 does not activate the DetailViewController.
To activate the DetailView (identical for mvc1 and mvc2) I do following
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
Item *theItem =[[self.items objectAtIndex:[indexPath section]]objectAtIndex:[indexPath row]];
[self.detailViewController setLabelsForItems:theItem];
}
Works very well as long the MasterViewController is defined in the appdelegate as member of the splitviewcontrollers array. So why doesn't the mvc2 activate the DetailViewController when it is on top of the stack?

tapping the rows of the tableview in mvc2 does not activate the
DetailViewController
Well, that isn't going to happen magically all by itself. It's up to you to respond when a row of the second table view is selected. You haven't shown any code for doing that, so one assumes you have none.
In other words, a split view controller does nothing in and of itself except for containing the master and the detail. Communicating between the master and the detail is up to you. The template shows you the sort of thing you'll need to do.

Related

self.navigationController is (null) in TableViewController which is on a TabBarController

I'm trying to create a small app in iOS. After the Login Page, I have a TabBarController and the first tab in it is a TableViewController. In didSelectRowAtIndexPath I'm trying to push one view controller for every selected row. The same view controller but self.navigationController is (null) when I print it using an NSLog and I'm not able to push the ViewController. HELP!!
Here's the sample Code:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
// Navigation logic may go here, for example:
// Create the next view controller.
PlayerDetailsViewController *detailViewController = [[PlayerDetailsViewController alloc] initWithNibName:#"PlayerDetailsViewController" bundle:nil];
detailViewController.myImg.image=[UIImage imageNamed:[self.arrNames objectAtIndex:indexPath.row]];
detailViewController.name.text=[self.names objectAtIndex:indexPath.row];
detailViewController.year.text=[self.draft objectAtIndex:indexPath.row];
detailViewController.height.text=[self.height objectAtIndex:indexPath.row];
detailViewController.weight.text=[self.weight objectAtIndex:indexPath.row];
detailViewController.pro.text=[self.pro objectAtIndex:indexPath.row];
detailViewController.ppg.text=[self.ppg objectAtIndex:indexPath.row];
detailViewController.apg.text=[self.apg objectAtIndex:indexPath.row];
detailViewController.rpg.text=[self.rpg objectAtIndex:indexPath.row];
NSLog(#"%#",self.navigationController);
[self.navigationController pushViewController:detailViewController animated:YES];
}
UPDATE: I got the navigationController to work but now, the data in the pushed view(PlayerDetails) in blank!!
It's just wrong that you haven't nested your firstViewController inside UINavigationController.
So I guess your code will be seems like this when creating UITabViewController.
[tabVC setViewControllers:[vc1,vc2,nil]];//tabVC = UITabViewController
And it need to be changed something like below;
UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:vc1];
[tabVC setViewControllers:[nav,vc2,nil]];
Assume that the instance of UITabBarController is tabVC and the instance of UITableViewController is tableVC.
UINavigationController *nav = [[UINavigationController alloc initWithRootViewController:tabVC];
or
UINavigationController *nav = [[UINavigationController alloc initWithRootViewController:tableVC];
[tabVC setViewControllers:#[nav]];
Both will fix your problem.

How do I create an array of View Controllers?

I'm brand new when it comes to app development so this might be a stupid question. So i have made a UI table. Each row is a different topic. I want to allow users to click on a table cell and it'll direct them to another view controller. All the view controllers will have different content arranged in different ways. Any idea how to implement this using storyboard or just programatically? Appreciate it!
To answer the main question of the post, here is how you create an array of view controllers:
// create your view controllers and customize them however you want
UIViewController *viewController1 = [[UIViewController alloc] init];
UIViewController *viewController2 = [[UIViewController alloc] init];
UIViewController *viewController3 = [[UIViewController alloc] init];
// create an array of those view controllers
NSArray *viewControllerArray = #[viewController1, viewController2, viewController3];
I'm not so sure this is what you actually need to do given your explanation, but without more information this answers the initial question.
You really don't want to create all the view controllers at once and have them sitting there in memory - you really only want to create them when they're actually needed - which is when the user selects the cell. You're going to want to do something like the following to achieve what you want to do:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[tableView deselectRowAtIndexPath:indexPath animated:YES];
if (indexPath.row == 0) {
// create the view controller associated with the first cell here
UIViewController *viewController1 = [[UIViewController alloc] init];
[self.navigationController pushViewController:viewController1 animated:YES];
}
else if (indexPath.row == 1) {
// create the view controller associated with the second cell here
UIViewController *viewController2 = [[UIViewController alloc] init];
[self.navigationController pushViewController:viewController2 animated:YES];
}
else {
// etc
}
}

How to load a different view to detail view in split view iOS

I was wondering how I can load a different view into the detailView from rootview..
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.row == 0) {
SecondViewController *svc = [[SecondViewController alloc] init];
//[self.navigationController pushViewController:svc animated:YES];
}
}
The splitviewcontroller has an array of 2 views one is root view and another one is detail view,and you can change the views in this array. If you alloc and init a view and replace it with either of these two then this view will replace the current view in the splitviewcontroller.
Use this code for loading different views for different cells of rootview. Change rootview's didSelectRowAtIndexPath method as follow:
- (void)tableView:(UITableView *)aTableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
UIViewController *localdetailViewController = nil;
if (indexPath.row==0)
{
DetailViewController *detailView=[[DetailViewController alloc] initWithNibName:#"DetailView" bundle:nil];
localdetailViewController=detailView;
detailView=nil;
}
if (indexPath.row==1)
{
SecondViewController *secondDetailView=[[SecondViewController alloc] initWithNibName:#"SecondDetailViewController" bundle:nil];
localdetailViewController=secondDetailView;
secondDetailView = nil;
}
UINavigationController *navController=[[UINavigationController alloc] init];
[navController pushViewController:localdetailViewController animated:YES];
YourSplitViewAppDelegate *delegate=[[UIApplication sharedApplication] delegate];
NSArray *viewControllers=[[NSArray alloc] initWithObjects:[delegate.splitViewController.viewControllers objectAtIndex:0],navController,nil];
delegate.splitViewController.viewControllers = viewControllers;
[localdetailViewController release];
[navController release];
}
My solution is using Using Xcode 7.1.1 for a universal app, using Storyboards, Auto Layout and Size Classes, building for iOS 9...
I used a version of Midhun MP's answer... within the current/original (not replacement/new/second) detail view controller's viewDidLoad method:
obviously first import the correct header file for your controller:
#import "NEW_DetailViewController.h"
then within viewDidLoad method:
- (void)viewDidLoad {
[super viewDidLoad];
<<OTHER_CODE>>
if (<<INSERT_THE_LOGIC_YOU_NEED_TO_TRIGGER_THE_REPLACEMENT_DETAIL_VC>>) {
NEW_DetailViewController *viewNewDetailVC = [[NEW_DetailViewController alloc] initWithNibName:#"NEW_DetailViewController" bundle:nil];
UINavigationController *navController = [[UINavigationController alloc] init];
[navController pushViewController:viewNewDetailVC animated:YES];
NSArray *arraySplitViewControllers = [[NSArray alloc] initWithObjects:[self.splitViewController.viewControllers objectAtIndex:0],navController,nil];
self.splitViewController.viewControllers = arraySplitViewControllers;
}
}
NOTE I could make this code work without loading a XIB file, building the entire replacement detail VC in code, with [[UIViewController alloc] init] instead of [[NEW_DetailViewController alloc] initWithNibName:#"NEW_DetailViewController" bundle:nil], but was frustrated that I could not make the XIB file load, so I persisted...
I attempted many variations from many different SO answers in attempting to solve this problem of successfully loading a XIB file as the replacement detail view controller in a split view controller.
Finally this answer by Travis M gave me the hint I needed...
Delete all the efforts you have attempted to date to create a new XIB file and subclass of UIViewController!
Create a new file and make it a subclass of UIViewController. See screenshot:
IMPORTANT ensure you check the checkbox "Also create XIB file".
You don't need to modify the new subclass of UIViewController, unless you'd like to code up the contents of the view. I only needed to add in a UILabel for my solution and I did this using Interface Builder.
Make sure you reference the replacement view controller NEW_DetailViewController in code as per my example above.
That's it!
In your XIB file you should end up with the following:

UINavigationController is NULL

I've had a good look at the Apple docs as well as similar Stack Overflow questions, but I am stuck on why my navigationController is null when using tab bars. I am trying to build most of the app from code, and am not using XIBs to insert a navigationController.
While debugging I have greatly simplified my app, down to two tabs. One tab holds a tableview and when a row is touched I'm expecting a detail page (from a XIB) to appear. Should be pretty simple. I am finding the value of self.navigationController is NULL when attempting to push the detail view, and of course it is then not working. I took the tab bar our completely and it works fine from a single view (the tableview). In this instance self.navigationController has a value.
AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// With Tab Bars
self.tabBarController = [[UITabBarController alloc] init];
ViewController *vc1 = [[ViewController alloc] initWithNibName:#"ViewController" bundle:nil];
vc1.tabBarItem.title = #"Words";
vc1.tabBarItem.image = [UIImage imageNamed:#"tab_feed.png"];
TextTableViewController *vc2 = [[TextTableViewController alloc] init];
vc2.tabBarItem.title = #"Text";
vc2.tabBarItem.image = [UIImage imageNamed:#"tab_live.png"];
UINavigationController* navController = [[UINavigationController alloc] initWithRootViewController:vc1];
NSArray* controllers = [NSArray arrayWithObjects:vc2, navController, nil];
tabBarController.viewControllers = controllers;
tabBarController.delegate = self;
window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
window.rootViewController = self.tabBarController;
[window makeKeyAndVisible];
return YES;
}
From TextTableViewController.m
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
TextViewController *detailViewController = [[TextViewController alloc] initWithNibName:#"TextViewController" bundle:nil];
Text *text = [[Text alloc] init];
text = [textArray objectAtIndex:indexPath.row];
detailViewController.TextID = text.textID;
NSLog(#"Nav Controller: %#",self.navigationController);
[self.navigationController pushViewController:detailViewController animated:YES];
NSLog(#"pushed");
}
I've also got two questions related to this problem.
(1) What is the purpose of this line. It doesn't appear to make a difference if it is in or out, and is absent from the Apple example.
tabBarController.delegate = self;
(2) When creating an array of tabs, one of the views is made the navigationController. Does it matter which tab it is or should this be a different view altogether not related to any tab and not visible. Is this where the problem lies?
In answer to question (1) about the tab bar controller's delegate, see the UITabBarControllerDelegate protocol reference. For the basic functionality of a tab bar controller, you don't need to bother with a delegate.
But let's say you want to do something special--for instance, save a document or reset an interface element to a default value--when the user changes tabs. You could make one of your classes, perhaps your app delegate or another controller class, conform to the UITabBarControllerDelegate protocol and implement tabBarController:didSelectViewController:
In your "answer" you asked if each tab will need its own UINavigation controller. That is absolutely correct. Basically, each tab is a completely independent hierarchy, so you need a separate UINavigation controller in each tab that requires one.
This should also imply the answer to your question (2) in the original post. You need to add the nav controller to the specific tab(s) that needs it.
OK I found it. The UINavigationController needs to be contained within the appropriate tab of the UITabBarController. So by making this coding change (below), a new UINavigationController is embedded in the tab with the tableview.
UINavigationController* navController = [[UINavigationController alloc] initWithRootViewController:vc2];
NSArray* controllers = [NSArray arrayWithObjects:vc1, navController, nil];
Which then begs the question: what if you need multiple examples of this - do you create a new UINavigationController for each tab that has a need for one, and mark each one as a rootViewController?

UINavigationController with UITabViewController

I'm building an app which's delegate has a UINavigationController (navigationController). The first view is a UITabViewController (tabView) which has a UINavigationController with a UIViewController with a UITableView which shows some contacts.
What I want to do is to push a new viewcontroller with the contact's info when tapping over a contact in the tableview (over the toppest navController)
I do the following in the appDelegate:
[self.window makeKeyAndVisible];
[self.window addSubview:[navigationController view]];
TabsView *tabsView = [[TabsView alloc] initWithNibName:nil bundle:nil];
[navigationController.view addSubview:[tabsView view]];
tabsView's first tab loads ContactsView.m which has a UINavigationController with all contacts and when someone clicks on one row, it is supposed to push the new view as this:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[table deselectRowAtIndexPath:indexPath animated:YES];
ContactSecondView * varContactSecondView = [[ContactSecondView alloc] initWithNibName:nil bundle:nil];
varContactSecondView.title = [[contactsArray objectAtIndex:indexPath.row] name];
[self.navigationController pushViewController:varContactSecondView animated:YES];
[varContactMapView release];
}
But nothing happens when touching in a row.
So I have different files: Delegate with UINavigationController <- UITabViewController <- UIViewController with UINavigationController with UITableView; and I want to push a ViewController into the first navigationcontroller.
How is supposed to access to delegate's navigationController? Am I doing it right?
Edit: If this gives any clue, when I do self.navigationController.title = #"Contacts"; in ContactsView.m, it's not changing the title of the topbar.
Thanks!
Two things.
It is not recommended to embed a UITabBarController in a navigation controller. It is ok to embed a UINavigationController within a UITabBarController. I know it would be a "nice to have" to embed your UITabBarController in the UINavigationController, but you may want to rethink your design so that you follow the iOS design philosophy.
Instead of adding the subview to the window in your appDelegate, try adding which ever controller you are using to the window's rootViewController property i.e,
self.window.rootViewController=navigationController;
I may be wrong, but I think you don't use the correct way to set your view controller in your navigation controller.
You do :
[navigationController.view addSubview:[tabsView view]];
I would use :
[navigationController setViewControllers:[NSArray arrayWithObject:tabsView] animated:NO];

Resources