Changing master view and detail view from uisplitview - objective-c

On my storyboard, my project begins with a split view that automatically assigns my custom UITableViewController (embedded in a navigation controller) as the detail view controller (done by relationship segue). How do I access the split view controls from my custom UITableViewController so I can change the master view controller views as appropriate?

UIViewController has a property splitViewController that is a reference to the split view controller the viewController is embedded inside. Since your table view controller is embedded inside a navigation controller, which is itself embedded inside a split view controller, you first need to get a reference to the nav controller, and then from that get its reference to the split view.
So in your custom tableViewController's code you can do this:
UISplitViewController *splitVC = [[self navigationController] splitViewController];
The from that you can get a reference to your masterViewController. The splitViewController has a property viewControllers which is an NSArray of two elements. The element at index zero is the master viewController. The element at index 1 is your detail view controller.
UIViewController *masterVC = [[splitVC viewControllers] objectAtIndex:0];
Note that if your master is a custom viewController subclass (which it probably is) you should cast it as such when you pull it out of the array.
If you want to relace the master view controller with a new viewController entirely, you can do that by creating a new array with your new master VC and the existing detail view controller and assigning it to your split view controller's viewControllers property:
UIViewController *detailVC = [[splitVC viewControllers] objectAtIndex:1];
NSArray *newViewControllerArray = [NSArray arrayWithObjects:newMasterVC, detailVC, nil];
splitVC.viewControllers = newViewControllerArray;

Related

Detect rootcontroller in iphone sdk

I have using two types of controller in my application i.e. NavigationController and presentViewController.
How can I detect base controller at any instance through code i.e I am using navigation or presentviewcontroller to transist one viewcontroller to another viewcontroller?
Try this for take rootviewcontroller of navigation:
UIViewController *topViewController = [self.navigationController topViewController];
for present modal view controller check out the 'presentingViewController' property of UIViewController and for navigation you can get the array of view controllers NSArray *ArryViewControllers=[self.navigationController viewControllers]; and then get the object at index 0 . this will be the root view controller of that navigation controller.

How to create simple table view controller in code only

I need the same type of table view controllers in my app many times and would like to create a more generic table view controller which I can use over and over again.
These table view controllers are quite simple and show only the contents of an array, put a check mark to the selected table view cell and return the index of the selected table view cell to the calling view controller after the Done button in the toolbar has been tapped.
Currently I create each one of these table view controllers directly in Storyboard and instantiate them by using segues.
Would it be possible to do this in code only (without using Storyboard or xibs)?
What would be the best way to instantiate and push them onto the navigation controller stack (each one will be shown in a view controller).
It's trivial to do this in code. You create your view controller class just like you normally would (extend UITableViewController). Implement all of the same table view data source and delegate methods. All of that is the same.
When you want to use the table view controller you just do:
MyTableViewController *vc = [[MyTableViewController alloc] init];
[self.navigationController pushViewController:vc animated:YES];
I would define your MyTableViewController init method like this:
- (id)init {
if ((self = [super initWithStyle:UITableViewStyleGrouped])) {
// any other initialization
}
return self;
}
BTW - I have an app with over 100 view controllers in it and I've never used Interface builder or storyboards. It's all code.
Would it be possible to do this in code only (without using Storyboard or xibs)?
Yes. Anything you can do in a storyboard or .xib file, you can do in code:
MyViewController *vc = [[MyViewController alloc] initWithNibName:nil bundle:nil];
Note: the default behavior for a view controller is to load its view from a .xib with the same name as the view controller's class when you pass nil for the .xib name, e.g. MyViewController.xib for the example above. So the line above creates the view controller in code, but will still load the view from the .xib. If you want the view created programmatically as well, override -loadView.

How to access the toolbar in a container view controller from its children?

I have the following view controller structure in my app:
A container view controller with a UIToolBar (which is not a navigation bar) has a UIPageViewController embedded and this UIPageViewController presents additional child view controllers:
container view controller (toolBar) -> PageViewController -> ViewController 1,
ViewController 2
I would like to add and remove buttons (BarButtonItems) to and from the toolbar on the container view controller depending on the child view controllers 1 and 2 presented.
How would you recommend that the child view controllers 1 and 2
access the toolbar in the container view controller to add and
remove buttons?
It seems that the toolbarItems property in the container view
controller is always 0 even though there are buttons in it. Any
ideas on why this could be? Is the toolbarItems property <> 0
only for navigation bars?
Edit
Based on the suggestion from user DBD I have added the following methods to the view controller CompanyViewController which has the toolbar and is the base class for my view controllers in a split views detail window (all my detail view controller inherit from CompanyViewController). One of these detail view controllers is used as a content view controller with a page view controller and the child view controllers described above.
CompanyViewController:
- (void)addToolBarItems:(NSArray *)buttonArray
{
NSMutableArray *items = [[self.toolbar items] mutableCopy];
[buttonArray enumerateObjectsUsingBlock:^(UIBarButtonItem *barButtonItem, NSUInteger idx, BOOL *stop) {
if (! [items containsObject:barButtonItem] ) [items insertObject:barButtonItem atIndex:0];
}];
[self.toolbar setItems:items animated:NO];
}
- (void)removeToolBarItems:(NSArray *)buttonArray;
{
NSMutableArray *items = [[self.toolbar items] mutableCopy];
[buttonArray enumerateObjectsUsingBlock:^(UIBarButtonItem *barButtonItem, NSUInteger idx, BOOL *stop) {
if ( [items containsObject:barButtonItem] ) [items removeObjectAtIndex:idx];
}];
[self.toolbar setItems:items animated:NO];
}
This is how I try to access the CompanyViewController from my child controllers:
- (CompanyViewController*)parentViewControllerWithToolbar
{
UIViewController *parentPageViewController = self.parentViewController;
CompanyViewController *parentContentViewController = (CompanyViewController*)parentPageViewController.parentViewController;
return (CompanyViewController*)parentContentViewController;
}
However, when trying to call the addToolBarItems method from the child view controller I can't get the method in Xcodes's autocomplete: It seems that I have no access to this method even though I imported #import "CompanyViewController.h".
Any suggestions on what I might be missing here?
I would suggest not accessing the toolbar directly. Instead, I'd suggest the container class having public methods like
- (void)addToolBarItem:(UIBarButtonItem *)button atIndex:(int)index;
- (void)removeToolBarItemAtIndex:(int)index;
Then all your child has to do is get the parent, possibly do a type cast and call the method.
Encapsulating your UI changes like this remove the need for potentially duplicate code in your child classes to modify the toolbar, protect much of your code from possible API changes by Apple and allow you to easily decide to swap out the tool bar to something like a custom control in the future without causing ripple code change effects.
EDIT:
I thought you were using a container view controller, not a UISplitViewController. I'm going to re-state my understanding so you can tell me if I got it wrong.
You have a UISplitViewController displaying a "master" and "detail" views. You want the detail view to call back to the master and request a toolbar change.
In detail view controller, you want the master, but calling "parent" just gives you the UISplitViewController. So you have to access the split view controller and get the master view controller from there.
MasterViewController *foo = [self.splitViewController.viewControllers objectAtIndex:0];
[foo removeToolBarItems:bar];
Which says, go get the UISplitViewController which I belong to (same as calling parent), then access the array of view controllers it holds. Take the first item in the array and assign as your master view controller. From the docs on the UISplitViewController
viewControllers
The array of view controllers managed by the receiver.
#property(nonatomic, copy) NSArray *viewControllers
Discussion
The
array in this property must contain exactly two view controllers. The
view controllers are presented left-to-right in the split view
interface when it is in a landscape orientation. Thus, the view
controller at index 0 is displayed on the left side and the view
controller at index 1 is displayed on the right side of the interface

View Controller behaves differently when set as 'initial view controller' vs. loading with presentModalViewController

My app has a map that tracks the user's location. This map will only appear under certain circumstances, and will dominate the user's attention until a particular task is complete, which is why the map isn't part of a navigation or tab bar UI.
If my map VC is set as the initial view controller in storyboard, it works fine. But if I try to load the map VC from elsewhere like this;
MapViewController *mapVC = [[MapViewController alloc] init];
[self presentModalViewController:mapVC animated:YES];
I just get a black screen.
I can confirm with NSLog that the VC is calling viewDidLoad and viewDidAppear, but the 'map' property of the VC is (null). I don't understand why (or how) I need to create the map property manually when using this technique, but it gets done for me when it is the initial VC.
The MapViewController instance in your storyboard is configured with a view hierarchy, including an MKMapView, and whatever else you did to configure that particular instance in the storyboard.
Now in this code which you show here, you are creating a completely new instance of MapViewController. It has no relationship to the instance in the storyboard other than they happen to be of the same class. So the one you create here with [[MapViewController alloc] init] has no view hierarchy (which is why you see a black screen), and none of the outlets or other configuration you may have made to the other MapViewController in your storyboard.
So what you want is to load that MapViewController that you've already set up from the storyboard. Assuming you are doing this from within a method in another view controller loaded from the same storyboard already, you can just do this:
// within some method on another vc from a scene in the same storyboard:
// given an identifier for the map view controller we want to load:
static NSString *mapVCIdentifier = #"SomeAppropriateIdentifier";
NSLog(#"Storyboard: %#",self.storyboard); // make sure this vc(self) was loaded from a storyboard
MapViewController *mapVC = [self.storyboard instantiateViewControllerWithIdentifier:mapVCIdentifier];
[self presentModalViewController:mapVC animated:YES];
And then back in the storyboard, just make sure you set the identifier for this map view controller to "SomeAppropriateIdentifier".
Hope that helps.

Access viewcontroller which resides inside a Tab Bar

I'm trying to access it in my AppDelegate by doing this
Course *rootController = (Course *)[navigationController tabcontroller];
but it won't work doesnt seem to get the "Course" root controller.
Thanks
It is not clear from your question how you have your views arranged and what you are trying to achieve. Usually a UITabBarController contains an array of root view controllers. Each view controller corresponding to a tab on the tab bar. Any or all of those view controllers could be a UINavigationController which itself can contain a stack of view controllers.
UITabBarController
|-UINavigationController -> [AViewController,.....]
|-UINavigationController -> [AnotherViewController,.....]
|-UINavigationController -> [AndAnotherViewController,.....]
The navigation controllers which in this case would be the root view controller for each tab can be accessed via the UITabBarController viewControllers property:
NSArray *rootViewControllers = [tabBarController viewControllers];
So if you want the root view controller of the first tab bar:
UINavigationController *rootViewController = [rootViewControllers objectAtIndex:0];