Using didSelectRowAtIndexPath with UISplitViewController and Google Maps - ios

I am using a UITableViewController inside of a UINavigationController for my Master and I'm using a UIViewController implementing the GMSMapViewDelegate inside of a UINavigationController for my detail side to display a google map. Currently the table view and the Google map are displaying in the UISplitViewController fine.
I am a beginner who recently finished reading Programming in Objective C and Big Nerd Ranch's guide for IOS 7. I can't figure out how to use the didSelectRowAtIndexPath method to change the camera position with the map. I know how to change the camera position, I've wrote NSLog calls to test whether my app was responding when tapping a particular row, but I can't figure out how to connect the two controllers. I thought about trying to make the controller holding the mapview a delegate for the UITableView, but I am confused as to how to connect the two. What options do I have to carry something like that out.
This is what my appdelegate file looks like.
...
mapviewController *mvc = [[mapViewController alloc]init];
locationTableController *ltc = [[locationTableController alloc]init];
UISplitViewController *svc = [[UISplitViewController alloc]init];
UINavigationController *sideNav = [[UINavigationController alloc]initWithRootViewController:ltc];
UINavigationController *mapNav = [[UINavigationController alloc]initWithRootViewController:mvc];
svc.delegate = mapNav;
svc.viewControllers = #[sideNav,mapNav];
....

I would keep your locationTableController as delegate and datasource for the table. You can use self.splitViewController to access the splitViewController, your mapNav is then at viewControllers[1] and your mapviewcontroller is rootViewController of mapNav. If you implement a changeCameraPosition method in your mapviewcontroller, you can call this from within didSelectRowAtIndexPath. So in didSelectRowAtIndexPath:
UISplitViewController *svc = self.splitViewController;
UINavigationController *mapNav = svc.viewControllers[1];
mapViewController *mvc = (mapViewController *)mapNav.rootViewController;
[mvc changeCameraPosition];
You may need to import the relevant .h files if not already done. Personally I would add some properties to the splitViewController to speed up accessing the other view controllers.

Related

Is it possible to wrap the rootViewController within another ViewController?

Right now I'm attempting to integrate this library with an existing app. However, the app already has a rootViewController. Is it possible to simply redirect the frontView this library requires to the current rootViewController that the app has? All I need to implement is the ability to swipe between the rearViewController and the frontViewController like in the first example the library gives.
Yes, but then you need to set the SWRevealController as the new rootViewController.
Here's some sample code to illustrate from application: didFinishLaunchingWithOptions: in your app delegate:
MYAppViewController *mainVC = [[MYAppViewController alloc] init];
// Before using SWRevealViewController I had this line:
// self.window.rootViewController = mainVC
// Used for the rear view of the SWRevealViewController
MYMenuViewController *menuVC = [[MYMenuViewController alloc] init];
SWRevealViewController *revealVC = [[SWRevealViewController alloc] initWithRearViewController:menuVC frontViewController:mainVC];
// Now set the SWRevealViewController as the root view controller
self.window.rootViewController = revealVC;
Just make your rootViewController class inherit from SWRevealViewController, then you wont need to wrap anything.
read the Documentation it explains what to do. You don't need to wrap anything.

Accessing tableViewController from appDelegate after adding SWRevealViewController

I have an app which displays a simple tableview and I wanted to add the SWRevealViewController as well.
In my appDelegate, before I added the SWReveal VC, I was setting my tableViewController like so...
In didFinishLaunchingWithOptions:
STRTableViewController *tableViewController = [(UINavigationController *)self.window.rootViewController viewControllers][0];
self.delegate = tableViewController;
and then again in the below method:
- (void)loadTableViewData
{
UINavigationController *navVC = (UINavigationController *)self.window.rootViewController;
STRTableViewController *tableVC = navVC.childViewControllers[0];
[tableVC loadTableData]
}
Obviously when I put the SWRevealViewController to the front of the line, this no longer works as it is now trying to call loadTableData from the wrong view controller.
I've tried several ways and keep coming up short. How do I go about accessing the tableViewController now that it is not the first view controller?
If you need more code or logs or anything I'll be happy to post additional info. I have a feeling the answer is right there, I just don't have the experience to see it.
Also, just to be clear, now in the storyboard it goes from Reveal View Controller to Navigation Controller (the tableview's nav VC/ sw_front) and also to the sw_rear VC. Before it simply started with the Navigation Controller.
Thanks!
There's a bunch of ways you can go about keeping a reference to this.
The simplest would be just to keep a reference to the view controller in the AppDelegate.m
So you add a property
#property (nonatomic, strong) STRTableViewController *tableViewController;
Then, whenever and wherever you are instantiating and setting that table view controller, just do something like:
AppDelegate *delegate = [[UIApplication sharedApplication] delegate];
delegate.tableViewController = justCreatedTableViewController;
You'll need to #import "AppDelegate.h" to access the app delegate in other classes where you want to do this.
Then to access it you can just do something like:
- (void)loadTableViewData
{
[self.tableViewController loadTableData]
}

iOS SplitView Controller, Programatically Change View With Multiple Storyboards

Hopefully someone can help.
I've got an app that uses the UISplitViewController however I've now had to start using multiple storyboards because i've got a large amount of views and Xcode was starting to run really slow. I've moved moved the UIViewControllers to multiple storyboards.
The Master View is built from static cells, so when the user selects the cell I normally change the view by pushing a segue.
I'm now wondering how to programmatically change the detail view of a UISplitViewController?
Thanks
Subclass UISplitViewController and set your root splitViewController to that class. Then add this method to your UISplitViewController subclass:
-(void)setDetailControllerTo:(UIViewController *)detailController withNavControllerTitle:(NSString *)title {
[detailController view]; // this line forces the viewDidLoad method to be called
if (title) {
UINavigationController *navController = [[UINavigationController alloc] init];
[navController pushViewController:detailController animated:YES];
detailController.title = title;
NSArray *viewControllers=#[self.mainController.viewControllers[0],navController];
self.mainController.viewControllers = viewControllers;
} else {
NSArray *viewControllers=#[self.mainController.viewControllers[0],detailController];
self.mainController.viewControllers = viewControllers;
}
}
To call this method do something like this from the master view controller:
FixedSplitViewController *splitController = (FixedSplitViewController*) self.splitViewController;
CurrentEventViewController *controller = [self.storyboard instantiateViewControllerWithIdentifier:#"CurrentEventViewController"];
// add any setup code here
[splitController setDetailControllerTo:controller withNavControllerTitle:#"Current Event"];
A lot of my projects require the splitviewcontroller to always show the master view so I use this subclass to keep the master view from hiding on portrait rotation.

use different xib items with the same UIViewController class

I have created a .xib file containing a UITabBar with 5 UITabBarItems inside. I would like 4 of the 5 tabs to link to the same UIViewController class since they have the exact same interface (only the data differentiate their looks).
Therefore it makes sense for me to instantiate my UIViewController 4 times, once per tab bar item. And then link each one of the UITabBarItems of the .xib with one instance of my UIViewController.
But I cannot figure out a way to take a reference of my xib tab bar items in my UIViewController and send the setTabBarItem message. How could I achieve that ? I was trying somehow to pass the .xib tab bar items on init (overwriting the init) but I didn't manage to reference them. I instantiate the controllers in the AppDelegate after the self.window stuff.
(If I say something weird here, not making sense with the usual iOS programming conventions, please let me know)
Use UITabBarController for this , not sure what you exactly want to do with the same UIViewController but UITabbarController will definitely work;
UITabBarController *tabBarController = [[[UITabBarController alloc] init] autorelease];
ViewController *viewController1 = [[ViewController alloc]initWithNibName:#"ViewController1"];
ViewController2 *viewController2 = [[ViewController2 alloc]initWithNibName:#"ViewController2"];
tabBarController.viewControllers = [NSArray arrayWithObjects:viewController1,viewController1,viewController1,viewController1,viewController2,nil];
self.window.rootViewController = tabBarController;

How to use a UISplitViewController as a Modal View Controller?

I am trying to display a UISplitViewController presenting it as a Modal View Controller in my iPad app. I manage to have it display, but for some reason there is a gap to the left of the modal view the size of a Status Bar which is also preserved when the orientation is changed.
Does anybody know why this is happening? Or if this is even possible? Maybe I'm just digging myself a huge hole.
Like for many of you, I needed a 'modal way' to use the UISplitViewController. This seems to be an old issue, but all I found in StackOverflow was at best an explanation why the problem happens when you attempt to do so (like the accepted answer above), or 'hack-arounds'.
However, sometimes it is also not very convenient to change much of your code-base and make a UISplitViewController the initial object just to get it's functionality up and running.
In turns out, there's a way to make everybody happy (including Apple guidelines). The solution that I found best, was to use the UISplitViewController normally, but when needed to be shown/dismissed, use the following approach:
-(void)presentWithMasterViewController: (UIViewController *) thisMasterViewController
andDetailViewController: (UIViewController *) thisDetailViewController
completion:(void(^)(void))completion
{
masterViewController = thisMasterViewController;
detailViewController = thisDetailViewController;
[self setViewControllers:[NSArray arrayWithObjects:masterViewController, detailViewController, nil]];
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.window.autoresizingMask = UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight;
self.window.rootViewController = self;
[self.window makeKeyAndVisible];
if(completion)
completion();
}
-(void)dismissViewControllerWithCompletion:(void (^)(void))completion
{
self.window = nil;
masterViewController = nil;
detailViewController = nil;
if(completion)
completion();
}
Where "window", is a property of your UISplitViewController subclass. And the system will take care of the rest!
For convenience/reference, I uploaded this as a UISplitViewController subclass to gitHub:
ModalSplitViewController
--EXAMPLE ON HOW TO USE --
mySplitViewController = [[ModalSplitViewController alloc] init];
mySplitViewController.delegate = self;
[mySplitViewController presentWithMasterViewController:masterViewController andDetailViewController:detailViewController completion:nil];
// when done:
[mySplitViewController dismissViewControllerWithCompletion:nil];
mySplitViewController = nil;
Side-note: I guess most of the confusion originates from the fact that
the UISplitView usage example from Apple documentation uses the window
created in the appDelegate, and for the fact that most people are not
so familiar with the window concept - because we normally don't need
to (they are created once in StoryBoards or boilerplate code).
Additionally, if you are doing state restoration, one should not
forget that programmatically-created UIViewControllers won't
automatically be restored by the system.
The stock UISplitViewController was designed for use as the root view controller only. Presenting one modally goes against the Apple Human Interface Guidelines and has a high probability of getting rejected by the App Review Team. In addition, you may receive the error:
Application tried to present Split View Controllers modally
Technically, this is what I did:
1/ Subclass a UIViewController ie. #interface aVC: UIViewController
2/ In the viewDidLoad, set up a splitViewController, ie. aSplitVC
3/ Then self.view = aSplitVC.view
After all, present aVC as modalViewController
I agree with Evan that this is slightly off-color for Apple, but I was able to complete a working version of this with the following solution:
UISplitViewController *splitVC = [[UISplitViewController alloc] init];
splitVC.delegate = VC2;
splitVC.viewControllers = [NSArray arrayWithObjects:navcon1, navcon2, nil];
UINavigationController *splitNavCon = [[UINavigationController alloc] init];
splitNavCon.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
[splitNavCon.view addSubview:splitVC.view];
VC2.splitParentViewController = splitNavCon;
[self presentViewController:splitNavCon animated:YES completion:nil];
This allowed me to have a working back button in the new UISplitViewController that was presented modally on the screen.
You'll notice that I actually pass the VC2 (the delegate of the UISplitViewController) its parent UINavigationController. This was the best way that I found I could dismiss the UISplitViewController from within the VC2:
[splitParentViewController dismissViewControllerAnimated:YES completion:nil];
I believe one can do the other way around: instead of custom controller presenting split controller, one can set up the split controller as the root window controller in storyboard, and on top of its view you can add your custom controller (ie, login screen) and remove it from the screen (removeFromSuperview for example) when it is needed.
That answer is not actually correct, because it not valid any longer since iOS8 and if you need to support even iOS7 you can do that like you put actually modally UIViewController which has a container as SplitView.
let mdSplitView = self.storyboard?.instantiateViewControllerWithIdentifier("myDataSplitView") as! MyData_SplitVC
self.addChildViewController(mdSplitView)
mdSplitView.view.bounds = self.view.bounds
self.view.addSubview(mdSplitView.view)
mdSplitView.didMoveToParentViewController(self)

Resources