SplitViewController UI layout - objective-c

I'm trying to create a UI whereby I have a SplitView with the Details area containing a TabBarController. The TabBarController will show 3 different types of detail for the item selected in the RootViewController of the SplitView.
So far, I've got the TabBar showing the SPlitView by doing the following;
1) Created a new SplitView based app.
2) Created a new TabBar based app
3) Copy the .xib, .h and .m files for the FirstView and SecondView controllers from the TabBar app into the SplitView app.
4) Added the following to my application delegate header file;
#class RootViewController;
#class DetailViewController;
#class FirstViewController;
#class SecondViewController;
#interface SplitViewTemplateAppDelegate : NSObject <UIApplicationDelegate> {
UIWindow *window;
UISplitViewController *splitViewController;
UITabBarController *tabBarController;
RootViewController *rootViewController;
DetailViewController *detailViewController;
FirstViewController *firstViewController;
SecondViewController *secondViewController;
}
#property (nonatomic, retain) IBOutlet UIWindow *window;
#property (nonatomic, retain) IBOutlet UISplitViewController *splitViewController;
#property (nonatomic, retain) IBOutlet RootViewController *rootViewController;
#property (nonatomic, retain) IBOutlet DetailViewController *detailViewController;
#property (nonatomic, retain) IBOutlet UITabBarController *tabBarController;
#property (nonatomic, retain) IBOutlet FirstViewController *firstViewController;
#property (nonatomic, retain) IBOutlet SecondViewController *secondViewController;
5) Opened up MainWindow.xib in IB and changed the class on the DetailsView to UITabController
6) Added the following code to my application delegate module file;
#import "FirstViewController.h"
#synthesize window, splitViewController, rootViewController, detailViewController, tabBarController, firstViewController, secondViewController;
-(void) makeTabBarController {
NSMutableArray *controllers = [NSMutableArray arrayWithArray:splitViewController.viewControllers];
int index = 0;
for (UIViewController *controller in splitViewController.viewControllers) {
if (index == 1) {
//NSLog(#"Index is: %#", index);
//NSLog(#"Controller name is: %#", controller.title);
UINavigationController *localNavController;
tabBarController = [[UITabBarController alloc] init];
NSMutableArray *localViewControllersArray = [[NSMutableArray alloc] initWithCapacity:2];
firstViewController = [[FirstViewController alloc] initWithNibName:#"FirstView" bundle:nil];
localNavController = [[UINavigationController alloc] initWithRootViewController:firstViewController];
localNavController.tabBarItem.title = #"First Tab";
[firstViewController release];
[localViewControllersArray addObject:localNavController];
[localNavController release]; // Retained by above array
secondViewController = [[SecondViewController alloc] initWithNibName:#"SecondView" bundle:nil];
localNavController = [[UINavigationController alloc] initWithRootViewController:secondViewController];
localNavController.tabBarItem.title = #"Second Tab";
[secondViewController release];
[localViewControllersArray addObject:localNavController];
[localNavController release]; // Retained by above array
tabBarController.viewControllers = localViewControllersArray;
[localViewControllersArray release]; // Retained thru above setter
//tabBarController.delegate = splitViewController;
[controllers replaceObjectAtIndex:index withObject:tabBarController];
}
index++;
}
splitViewController.viewControllers = controllers;
}
7) Added the following to the didFinishLaunchingWithOptions method;
[self makeTabBarController];
So now I get an out of the box SplitView with a tab bar controller on the right with two tabs in it. The tabs work for switching between the views.
A couple of things that I am now struggling with are;
The button to fire the Popover is missing, do I need to add this to each tab view?
How do I hook the RootViewController with the TabBarController so that details for the selected item is shown?

not sure if you're still looking for an answer for this. I ran into a very similar issue with the question about multiple detail views. In the end, I found and used this solution from the apple developer site:
SubstitutableDetailViewController
Their solution is very straightforward to implement and very understandable.
With regards to the second part of your question, you could have the TabBarController Delegate inform the view on the left hand side of the split view who the current detail view is. It could then use that information to provide updates to the proper view.

Related

Double click on row in NSTableView doesn't display the new view

I have an os x app that uses core data.
I have 3 .xib files in my app, those are:
1. MainMenu.xib
2. MasterTableViewController.xib
3. DetailViewController.xib
When started , app displays a view that has NSTableView with couple of records in it.
I name that view MasterTableViewController
I want when user double click on the row, to hide the "master" view and to display my "detail" view. I named that view DetailViewController.
When double clicked on the row in the NSTableView in the "master" view,nothing happen, "master" view remains visible. What I want is "master" view to dissapear, and "detail" view to appear.
Here is the code that I have right now, and more explanations follows:
AppDelegate.h
#import <Cocoa/Cocoa.h>
#interface AppDelegate : NSObject <NSApplicationDelegate>
#property (readonly, strong, nonatomic) NSPersistentStoreCoordinator *persistentStoreCoordinator;
#property (readonly, strong, nonatomic) NSManagedObjectModel *managedObjectModel;
#property (readonly, strong, nonatomic) NSManagedObjectContext *managedObjectContext;
#property (nonatomic,strong) NSViewController *mainAppViewController;
#property (weak) IBOutlet NSView *mainAppView;
- (void)changeViewController:(NSInteger)tag;
#property (weak) IBOutlet NSTableView *websitesTableView;
- (void)tableViewDoubleClick:(id)nid;
#end
AppDelegate.m
#import "AppDelegate.h"
#import "MasterTableViewController.h"
#import "DetailViewController.h"
#interface AppDelegate ()
#property (weak) IBOutlet NSWindow *window;
- (IBAction)saveAction:(id)sender;
#end
#implementation AppDelegate
NSString *const masterTable = #"MasterTableViewController";
NSString *const detail = #"DetailViewController";
-(void)awakeFromNib {
[_websitesTableView setTarget:self];
[_websitesTableView setDoubleAction:#selector(tableViewDoubleClick:)];
}
- (void)tableViewDoubleClick:(id)nid {
NSInteger rowNumber = [_websitesTableView clickedRow];
NSTableColumn *column = [_websitesTableView tableColumnWithIdentifier:#"websiteUrl"];
NSCell *cell = [column dataCellForRow:rowNumber];
NSInteger tag = 2;
[self changeViewController:tag];
}
- (void)changeViewController:(NSInteger)tag {
[[_mainAppViewController view]removeFromSuperview];
switch (tag) {
case 1:
self.mainAppViewController = [[MasterTableViewController alloc]initWithNibName:masterTable bundle:nil];
break;
case 2:
self.mainAppViewController = [[DetailViewController alloc]initWithNibName:detail bundle:nil];
break;
}
[_mainAppView addSubview:[_mainAppViewController view]];
[[_mainAppViewController view] setFrame:[_mainAppView bounds]];
[[_mainAppViewController view] setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
}
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
// automatically run the master table view controller
NSInteger tag = 1;
[self changeViewController:tag];
}
Now, some of you may wondering, where is the rest of the code. I have ommited the boiler plate code for the core data below in the AppDelegage.m, since it is unchanged. I used binding to make my NSTableView to work and to display my records, so MasterTableViewController.h and .m files are empty, and same is true for the DetailViewController.h and .m file.
Important note - What i can't understand here: If I change the tag in 2 in the applicationDidFinishLaunching method, detail view is displayed normally, but if i switch it back on 1, and then double click on the row, "master" view (with the NSTableView) remains visible, and nothing happen (views are not swapped)
Anyone can help me to find out what is wrong with my code?
Regards, John
You apparently had a second instance of your AppDelegate class instantiated in the MasterTableViewController.xib file. There should be only one AppDelegate instance and that's the one in MainMenu.xib. So, it shouldn't be in MasterTableViewController.xib.
One of the instances was receiving the double-click action method from the table, but the other one was the one with the outlet to the main window.
You need(ed) to get rid of the second instance and find another way to access the app delegate from the MasterTableViewController.

uninitialized property in prepareForSegue

I'm having some difficulty managing a segue. My view controller hierarchy is as follows:
BHGSplitViewController (subclass of UISplitViewController)
UINavigationController
MasterViewController (subclass of UITableViewController)
UINavigationController
DetailViewController (subclass of UIViewController)
These have the following properties:
BHGSplitViewController:
#interface BHGSplitViewController : UISplitViewController
#property (nonatomic, strong) MasterViewController* masterVC;
#end
MasterViewController:
#interface MasterViewController : UITableViewController
#property (strong, nonatomic) MenuDataSource *menuDataSource;
#property (strong, nonatomic) DetailViewController *detailViewController;
#end
DetailViewController:
#interface DetailViewController : UIViewController
#property (strong, nonatomic) NSURL* URL;
#property (strong, nonatomic) IBOutlet UIWebView *webView;
#end
These are related in my main storyboard through relationship segues.
After the initial app launch, I modally display a login view controller. After login, I segue back to my BHGSplitViewController, but I need to set some data. In the loginViewController's prepareForSegue method I attempt the following:
BHGSplitViewController *splitVC = [segue destinationViewController];
splitVC.masterVC.menuDataSource.var = someValue;
But, when debugging, splitVC.masterVC = nil, so obviously attempting to set a value on it is not going to work. How do I set-up and retain those relationships?
I inherited a version of this app that was built with storyboards. I'm guessing that I need to start intializing these properties. What's the best way to do that with storyboards? Do I need to override initWithCoder:? Should I be setting these properties in viewDidLoad?
Edit: Explanation of segue
So, I'm loading up my BHGSplitViewController in my AppDelegate and setting it up to handle collapses, etc. After that, I need to immediately display a modal view for login. I need this to animate in like a push (but Apple won't allow that), so I'm trying to hack my way around that by using a custom segue (which is hacky and I hate it):
Present Segue:
- (void)perform {
UIViewController *srcViewController = (UIViewController *) self.sourceViewController;
UIViewController *destViewController = (UIViewController *) self.destinationViewController;
UIView *prevView = srcViewController.view;
UIView *destView = destViewController.view;
UIWindow *window = [[[UIApplication sharedApplication] delegate] window];
[window insertSubview:destView aboveSubview:prevView];
[destView enterLeft:0.1 then:^{
[srcViewController presentViewController:destViewController animated:NO completion:nil];
}];
}
Dismiss Segue:
- (void)perform {
UIViewController *srcViewController = (UIViewController *) self.sourceViewController;
UIViewController *destViewController = self.destinationViewController;
UIView *prevView = srcViewController.view;
UIView *destView = destViewController.view;
UIWindow *window = [[[UIApplication sharedApplication] delegate] window];
[window insertSubview:destView aboveSubview:prevView];
[destView enterRight:0.1 then:^{
[srcViewController presentViewController:destViewController animated:NO completion:nil];
}];
}
You can set the properties in viewDidLoad. initWithCoder is definitely too early since the split view controller is instantiated first, and then the other controllers it houses. So, you can do this in viewDidLoad of BHGSplitViewController,
-(void)viewDidLoad {
[super viewDidLoad];
UINavigationController *nav = (UINavigationController *)self.viewControllers.firstObject;
self.masterVC = (MasterViewController *)nav.topViewController;
}

set UISearchBar Focused programmatically

i have 2 tabs on a UITabBarController, a navigation inside each tab, and on the first one a tableViewController with searchBar.
On the second tab, i have another searchBar, but, i want to set the onClick of this one, go to the first tab and focus the search bar.
i currently can redirect the tap on the searchbar to the other tab, but i dont know how to set the focus.
-(BOOL)searchBarShouldBeginEditing:(UISearchBar *)searchBar{
UINavigationController *tmp = [self.tabBarController.viewControllers objectAtIndex:0];
[tmp popToRootViewControllerAnimated:NO];
UIViewController *vc = tmp.topViewController;//THIS ONE
[self.tabBarController setSelectedIndex:0];
return NO;
}
The UIViewController that i've got on the code, has the UISearchBar object that i need, i mean, the superClass.h is:
#import <UIKit/UIKit.h>
#interface MasterTableViewController : UITableViewController <UISearchBarDelegate>
#property (nonatomic, strong) NSMutableArray *objects;//stuff
#property (nonatomic, strong) NSMutableArray *results;//stuff
#property (nonatomic, strong) IBOutlet UISearchBar *searchBar;//THIS IS MY UISearchBar !
#end
my problem is that i can't get that UISearchBar, how should i do it ?
and how can i say it to be focused from the searchBarShouldBeginEditing Method on the other tab ?
Modify your searchbar delegate method and check below:-
- (BOOL)searchBarShouldEndEditing:(UISearchBar *)searchBar;

Xcode: Passing label to second view?

I have two location labels on my first view controller I need them to be displayed on another view controller when opened, what is the best way to implement this?
The labels i want to pass look like this:
latitudeLabel.text = [NSString stringWithFormat:#"latitude=%f", location.coordinate.latitude];
longitudeLabel.text = [NSString stringWithFormat:#"longitude=%f", location.coordinate.longitude];
i had created a sample for sharing data between classes.. All you need to do is explained here
Before you push secondViewController you can add your labels as subviews of second ViewController's view
SecondViewController *cntr = [[SecondViewController alloc] initWithNibName:#"" bundle:nil];
[cntr.view addSubview latitudeLabel];
[cntr.view addSubview longitudeLabel];
[self.navigationController pushViewController:cntr animated:YES]
You have to create identical labels on both view controllers and pass only the string data. You can pass parameters from one class to another with properties (since they are public).
In first view controller .m:
- (void)openSecondViewController {
SecondViewController *controller = [SecondViewController alloc] init];
controller.data = [NSArray arrayWithObjects:#"First String", #"Second String", nil];
[self.navigationController pushViewController:controller];
}
In second view controller .h:
#interface SecondViewController : UIViewController
#property (weak, nonatomic) id data;
#property (weak, nonatomic) IBOutlet UILabel *label1;
#property (weak, nonatomic) IBOutlet UILabel *label2;
#end
In second view controller .m:
- (void)setData:(id)data {
self.label1.text = [data objectAtIndex:0];
self.label2.text = [data objectAtIndex:1];
}

Correct way of setting up UINavigationController and its RootViewController in IB

I have the following structure in my iPad App:
Application
UINavigationController (Providing the top bar with UIBarButtons etc.)
Initial Login screen
Second login screen
I am not sure how I should now set this up in Interfacebuilder correctly. My guess would be that I create two ViewControllers:
LoginVC1: (This one should also include the NavigationController since it is the first of the two screens)
LoginVC2: Based on some delegate callback from LoginVC1 my application would push to this ViewController.
This is my LoginVC1 in IB:
LoginVC1 http://k.minus.com/jpamEAFkBjpKT.png
And when I present it modally it looks like this which is not what I want:
Result http://k.minus.com/jHAYRnY788jFt.png
The result:
Nor the title of the ViewController nor the Cancel button is shown
The view seems to be empty despite my view in IB
I have set the Presentation mode of the UINavigationController to FormSheet which is ignored too since it is displayed in fullscreen.
What am I doing wrong?
Kjuly I simply do presentModalViewController to show the dialog, but
since my UINavigationControler contains a UIViewController (See
nib-screen), I would assume that this ViewController is displayed when
the modal is shown.
Not always Besi. Adding it to the UINavigationController Hierarchy doesn't add is as the rootViewController. That must be done in code like this:
UIViewController *rootViewController = [[[ExamplesViewController alloc] init] autorelease];
UINavigationController * navController = [[UINavigationController alloc] initWithRootViewController:rootViewController];
Also, check that you are presenting the NavigationController, and not the ViewController modally.
To solve the NavigationBar title issue, try setting the self.title property in the
-(void)viewDidload method and see if that works. If not, try self.//instance of UINavigationController//.navigationBar.title = #"string".
As for the other buttons not showing up, my guess is that if setting the root controller in code doesn't help, then you only made reference to them in the .h, instead of instantiating them. So either call something like this in the .h:
//.h
#implementation ExampleViewController: UIViewController <UITextFieldDelegate> {
IBOutlet UIBarButtonItem * CancelButton;
IBOutlet UITextField * usernameField;
IBOutlet UITextField * passwordField;
IBOutlet UIButton * loginButton;
}
#property (nonatomic, retain) IBOutlet UIBarButtonItem * CancelButton;
#property (nonatomic, retain) IBOutlet UITextField * usernameField;
#property (nonatomic, retain) IBOutlet UITextField * passwordField;
#property (nonatomic, retain) IBOutlet UIButton * loginButton;
Then connect the outlets in the XIB, or instantiate the buttons in the .m with:
//.m
-(void)viewDidLoad {
//do stuff
CancelButton = [[[UIBarButtonItem alloc]initWithTitle:#"Cancel" style:UIBarButtonItemStyleDone target:self action:#selector(dismissSelf)]autorelease];
//do more stuff
}