MMDrawerController and instantiating many view controllers - ios

This is a specific MMDrawerController question, although probably relates to other iOS drawer controllers out there.
I've got my MMDrawerController working nicely, and can load different view controllers into the 'center' by selecting a row in my drawer tableview.
However, I want to avoid instantiating my view controllers every time I select a menu item in my drawer. It seems inefficient, especially if the user will switch between screens many times during a session. I'm guessing a better way is to store the (instantiated) view controllers I'm using as a variable and to reuse? I have quite a few view controllers to potentially load into the center, each of which will do some 'work' initially in viewDidLoad.
Here is how I am loading a new view controller into the center (in my DrawerViewController.didSelectRowAtIndexPath method):
MyViewConroller * newCenter = [[MyViewConroller alloc] init];
UINavigationController * nav = [[MMNavigationController alloc] initWithRootViewController:newCenter];
if(indexPath.row==0){
[self.mm_drawerController
setCenterViewController:nav
withCloseAnimation:YES
completion:nil];
}
Thoughts/comments about the best approach of loading these view controllers would be appreciated.

This method is working well for me and there is no need to instantiate a new view controller or navigation controller every time the user changes views.
Declare a mutable array to hold your navigation controllers:
#property (nonatomic, strong) NSMutableArray *navigationControllerArray;
Initialise and fill the array with the same number of empty strings as you have view controllers:
- (void)viewDidLoad
{
[super viewDidLoad];
self.navigationControllerArray = [[NSMutableArray alloc] initWithObjects:#"",#"",#"",nil];
}
In your UITableViewDelegate, check to see if the object at the selected row is a UINavigationController. If it is not, instantiate a new navigation controller and replace the empty string in your navigation controller array with it.
Set this navigation controller as the center view controller:
#pragma mark - UITableViewDelegate
-(NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSObject *navigationController = [self.viewControllerArray objectAtIndex:indexPath.row];
if (![navigationController isKindOfClass:[UINavigationController class]]) {
UIViewController *newViewController;
switch (indexPath.row) {
case 0:
newViewController = (UIViewController *)[[AccountListTableViewController alloc] init];
break;
case 1:
newViewController = (UIViewController *)[[PageDetailViewController alloc] init];
break;
case 2:
newViewController = (UIViewController *)[[LoginViewController alloc] init];
break;
default:
newViewController = (UIViewController *)[[AccountListTableViewController alloc] init];
break;
}
navigationController = (UINavigationController *)[[UINavigationController alloc] initWithRootViewController:(UIViewController *)newViewController];
[self.viewControllerArray replaceObjectAtIndex:indexPath.row withObject:navigationController];
}
[self.mm_drawerController setCenterViewController:(UINavigationController *)navigationController withCloseAnimation:YES completion:nil];
return indexPath;
}

Related

SWRevealViewController Set Front VC Objective-C

I implemented SWRevealViewController in my project for Side menu item. Basically am app is kind of Music application. Songs from Home screen will remain playing continuously if the user in some other screen or in background. Am following this tutorial AppCoda (http://www.appcoda.com/ios-programming-sidebar-navigation-menu/)
When the app launching the Home Screen will be launched also start to play song.
If the user goes to another screen like playlist from the side menu item. The Home screen is in Stack and the song is playing perfectly.
The Playlists screen is in Front.
Again I go the Home screen from Side menu item. The new instance is creating instead of going to the already created Home screen. Now, am
able to listen two songs at a time. One from first Home Screen and
another one from new Home Screen.
This is happening for all screens. How can I solve this issue? I want only one screen from the stack instead of creating the same screen in many times.
Here is my Code from Side menu tableview Controller,
- (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.section == 1) {
UINavigationController *navController;
if (indexPath.row == 0) {
ViewController *homeVC = [self.storyboard instantiateViewControllerWithIdentifier:#"ViewController"];
navController = [[UINavigationController alloc] initWithRootViewController:homeVC];
[navController setViewControllers: #[homeVC] animated: YES];
} else if (indexPath.row == 1) {
SongsListViewController *songsListVC = [self.storyboard instantiateViewControllerWithIdentifier:#"SongsListViewController"];
navController = [[UINavigationController alloc] initWithRootViewController:songsListVC];
[navController setViewControllers: #[songsListVC] animated: YES];
} else if (indexPath.row == 2) {
PlayListViewController *songsListVC = [self.storyboard instantiateViewControllerWithIdentifier:#"PlayListViewController"];
navController = [[UINavigationController alloc] initWithRootViewController:songsListVC];
[navController setViewControllers: #[songsListVC] animated: YES];
}
[self.revealViewController setFrontViewController:navController];
[self.revealViewController setFrontViewPosition: FrontViewPositionLeft animated: YES];
}
}
Looking forward your help. Thanks in advance.
In fact you need change the playing handling to singleton, not the viewcontroller
in SWSongPlayManagerSingleton.h
#import <Foundation/Foundation.h>
#interface SWSongPlayManagerSingleton : NSObject
+ (instancetype)sharedInstance;
#end
in SWSongPlayManagerSingleton.m
#implementation SWSongPlayManagerSingleton
+ (instancetype)sharedInstance {
static SWSongPlayManagerSingleton *singleton = nil;
static dispatch_once_t token;
dispatch_once(&token, ^{
singleton = [[self alloc] init];
});
return singleton;
}
#end
now you access the SWSongPlayManagerSingleton instance by
[SWSongPlayManagerSingleton sharedInstance] which always gives you the same instance.
Fact that you can listen multiple songs for some cases, I think your player is not deallocated properly when it is supposed to. Also if you want to play/control the player regardless of where you are in the app, you should find a global place or singleton to handle that. Maybe in the Side menu table view controller??
I'm not familiar with SWRevealViewController but what I can say is that you are creating a new instance every time you select a menu in the table view. If you don't want that, you should have homeVC, songsListVC, and songsListVC as properties of Side menu table view controller and should do lazy load. i.e) You only create them if it is nil.
Actually, you already have all the view controllers in the stack. You don't have to create new view controllers, as you are doing with "instantiateViewControllerWithIdentifier" method.
You have to reference the navigation controller object and you can access the array of view controllers in the stack with viewControllers property of the navigation controller object.
UINavigationController *navController = self.navigationController;
This should get the reference to your navigation controller.
NSArray *viewControllers = navController.viewControllers;
This should get you the viewControllers already present in the stack. You can then loop through this viewControllers' array to check for an instance of your HomeViewController using
for (UIViewController *obj in viewControllers)
{
if([obj isKindOfClass:[HomeViewController class]])
{
// Your code
}
}

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:

Why won't self.navigationController pushViewController work?

I have a
UIViewController-> UINavigationBar + UITableView
Just a bit more explanation
I made it through UIBuilder..
1: Created a New UIViewController with XIB-file
2: Using UIBuiler i put a UINavigationController
3: Then i put UITableView underneath the navigationBar
so it gave me..
A: UIViewController-> UINavigationBar + UITableView
Now i am loading the data in UITableView from a Webservice which is working fine.
I again made a xib with sam config which is
B: UIViewController-> UINavigationBar + UITableView
So now when i try to push view B on view A using below code...it wont at all work...
SelectSiteViewController *siteViewController = [[SelectSiteViewController alloc] initWithNibName:#"SelectSiteViewController" bundle:nil];
[self.navigationController pushViewController:siteViewController animated:YES];
When i checked the UINavigationController *nav = self.navigation
nav is 0x0 that is i assume NIL.
Can anybody tell me whats wrong in here.. why is it nil...and how can i make it work..
Thanks a Lot....I would really appreciate any help
In UIBuilder verify that UINavigationController is referenced by the File's owner.
Got it.
I changed the Architecture a little bit.
I made a New UIViewController class with its xib. And coded new UINavigationController.
- (void)viewDidLoad {
[super viewDidLoad];
UINavigationController *navigationController
navigationController = [[UINavigationController alloc] init];
[self.view addSubview:navigationController.view];
switch (whichViewController) {
case 1:
viewController = [[xxxx alloc] init];
break;
case 2:
viewController = [[xxx1 alloc] init];
break;
default:
break;
}
[navigationController pushViewController:viewController animated:NO];
[viewController release];
}
And pushing the view in the Switch Statement....
I hope this makes sense ......
Thanks jamihash
So you are adding the tableview to the navigation controller right? This is how:
tableView = [[UITableViewController alloc] initWithStyle:UITableViewStylePlain];
navigationController = [[UINavigationController alloc] init];
[navigationController pushViewController:tableView animated:NO];
The tableview gets added as the rootview to the navigation controller. And then on selecting a row if you wish to push another viewcontroller use the
self.navigationController pushViewController: newViewController animated:YES];
inside the didSelectRowAtIndex method.
NOTE: its UITableViewController *tableView and UINavigationController *navigationController by declaration. So code accordingly for your table.
why UIViewController-> UINavigationBar + UITableView ?
I suggest you another approach
->UITableViewController A -> Embedded with a Navigation Controller, then after populate tableview you can pass data to
->UITableViewController B by
[[self storyboard]instantiateViewControllerWithIdentifier:#"ControllerB"];
then, in storyboard, drop a tableView B and in Identity->storyboard Id put some id.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *sUrl= #"<#string#>";
if (indexPath.row == 0) sUrl= #"http://www.apple.com/";
if (indexPath.row == 1) sUrl= #"http://www.google.com/";
if (indexPath.row == 2) sUrl= #"http://www.times.uk/";
NSMutableArray *selectedObject = [arrayOpenMale objectAtIndex:0];
NSLog(#"%# is the selected object.",selectedObject);
SeriesAdetailVC *dvc = [[self storyboard]instantiateViewControllerWithIdentifier:#"DetailView"];
dvc.strings7 = [NSURL URLWithString:sUrl];
[self.navigationController pushViewController:dvc animated:YES];
}
hope help

UISplitViewController in a TabBar ( UITabBarController )?

I am in kind of situation that I need to start with a tab based application and in that I need a split view for one or more tabs. But it seems that split view controller object can not be added to the tabbarController. (Although tabbar object can be added to the splitviewcontroller).
The problem can be seen otherways: I have a full screen in the left part I have a table view when any row is selected in the table a popover should come out pointing that row. Now when any row in the popover is selected the rows in this popover comes to the left under the selected row (only this row would be visible) and another popover comes out from the selected row. (Breadcrumb navigation type)
I think I am clear in what I explained. So guys any ideas or work arounds?
Please let me know if I am not clear in my question.
Thanks,
Madhup
Using the interface builder, create a split view controller and a tab bar controller and link them to your outlets:
#property (nonatomic, retain) IBOutlet UITabBarController *tabBarController;
#property (nonatomic, retain) IBOutlet UISplitViewController *splitViewController;
In your app delegate didFinishLaunchingWithOption, assign your split view controller to the tab bar controller:
splitViewController.tabBarItem = [[[UITabBarItem alloc] initWithTitle:#"Title" image:nil tag:0] autorelease];
NSArray *controllers = [NSArray arrayWithObjects:splitViewController, /* other controllers go here */ nil];
tabBarController.viewControllers = controllers;
[window addSubview:tabBarController.view];
[window makeKeyAndVisible];
This will create a tab bar controller (with only 1 tab in this case), which is displayed correctly in all orientations.
I've written up a subclass for the UISplitViewController that will listen for changes to device orientation and orient itself accordingly. With this class, I can now place split views within a UITabBarController and each split view will behave correctly upon rotation, even if it's not the frontmost tab. I've successfully deployed this in TexLege and it was approved for use in the App Store, but your mileage may vary. Please see the repository at Github.
Feel free to fork and modify it, and I'm always interested in hearing comments (or complaints) about it. https://github.com/grgcombs/IntelligentSplitViewController
I made a sample application. and found we can do it programmatically like:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
NSMutableArray *array = [NSMutableArray array];
NSMutableArray *tabArray = [NSMutableArray array];
UISplitViewController *splitViewConntroller = [[UISplitViewController alloc] init];
MainViewController *viewCont = [[MainViewController alloc] initWithNibName:#"MainViewController" bundle:nil];
[array addObject:viewCont];
[viewCont release];
viewCont = [[DetailViewController alloc] initWithNibName:#"DetailViewController" bundle:nil];
[array addObject:viewCont];
[viewCont release];
[splitViewConntroller setViewControllers:array];
[tabArray addObject:splitViewConntroller];
[splitViewConntroller release];
array = [NSMutableArray array];
splitViewConntroller = [[UISplitViewController alloc] init];
viewCont = [[MainViewController alloc] initWithNibName:#"MainViewController" bundle:nil];
[array addObject:viewCont];
[viewCont release];
viewCont = [[DetailViewController alloc] initWithNibName:#"DetailViewController" bundle:nil];
[array addObject:viewCont];
[viewCont release];
[splitViewConntroller setViewControllers:array];
[tabArray addObject:splitViewConntroller];
[splitViewConntroller release];
// Add the tab bar controller's current view as a subview of the window
[tabBarController setViewControllers:tabArray];
[window addSubview:tabBarController.view];
[window makeKeyAndVisible];
return YES;
}
Hope this helps.
To let a tabbarcontroller appear as a master view for splitviewcontroller you should rewrite tabbarcontroller so that it will support or orientations(so say, using a category for the class UITabBarController)
See my post about retrofitting split view controllers to an existing tab bar interface: http://markivsblog.blogspot.com/2010/04/retrofitting-ipad-uisplitviewcontroller.html
I created a UITabBarController subclass which properly propagates the rotation messages to all UISplitViewControllers it contains. This maintains the correct internal state of the UISplitViewControllers. However, one of the SplitViewController delegate methods is not called if the SplitViewController is not visible, so I account for this in the detail view controller viewWillAppear method. I've confirmed this works in iOS5.0 - iOS6.1
OSTabBarController.m
#import "OSTabBarController.h"
#implementation OSTabBarController
-(void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration{
[super willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration];
for(UIViewController *targetController in self.viewControllers){
if(targetController != self.selectedViewController && [targetController isKindOfClass:[UISplitViewController class]]){
[targetController willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration];
}
}
}
-(void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation{
[super didRotateFromInterfaceOrientation:fromInterfaceOrientation];
for(UIViewController *targetController in self.viewControllers){
if(targetController != self.selectedViewController && [targetController isKindOfClass:[UISplitViewController class]]){
[targetController didRotateFromInterfaceOrientation:fromInterfaceOrientation];
}
}
}
#end
DetailViewController
#implementation OSDetailViewController
-(void)viewWillAppear:(BOOL)animated{
//the splitViewController:willHideViewController:withBarButtonItem:forPopoverController: may not have been called
if(!UIInterfaceOrientationIsPortrait(self.interfaceOrientation)){
self.navigationItem.leftBarButtonItem = nil;
}
}
#pragma mark - UISplitViewControllerDelegate Methods
- (void)splitViewController:(UISplitViewController *)splitController willHideViewController:(UIViewController *)viewController withBarButtonItem:(UIBarButtonItem *)barButtonItem forPopoverController:(UIPopoverController *)popoverController
{
[self.navigationItem setLeftBarButtonItem:barButtonItem animated:YES];
}
- (void)splitViewController:(UISplitViewController *)splitController willShowViewController:(UIViewController *)viewController invalidatingBarButtonItem:(UIBarButtonItem *)barButtonItem
{
[self.navigationItem setLeftBarButtonItem:nil animated:YES];
}
#end
Keep in mind that OS 3.2 does not provide proper support for a splitview as a tabbar view.
You can make it "work" but it will have bugs - the biggest is that an orientation change made on another tab's view will often not propagate to the splitview tab view properly, making the view go wacky when you go back to it (left side view takes over the screen, or the barbutton item is missing, etc.).
I've reached the conclusion that I have to create my own splitview for use in a tabBarController because of this issue.
I had heard rumors that Apple was working on a fix but it's been months now and no iPad OS updates have occurred - maybe OS 4 for the iPad will address it.
You can use IB to build tabtab and modify tabs to splitviewcontroller.
-(void) makeSplitViewController {
NSMutableArray *controllers = [NSMutableArray arrayWithArray:tabBarController.viewControllers];
int index = 0;
for (UIViewController *controller in tabBarController.viewControllers) {
if ([controller.tabBarItem.title isEqualToString:#"Stock"]) {
stockDetailController = [[StockDetailController alloc] initWithNibName:#"StockDetailController" bundle:nil];
stockMasterController = [[StockMasterController alloc] initWithStyle:UITableViewStylePlain];
stockMasterController.navigationItem.title = date;
stockMasterController.stockDetailController = stockDetailController;
UINavigationController *nav = [[[UINavigationController alloc] initWithRootViewController:stockMasterController] autorelease];
splitViewController = [[UISplitViewController alloc] init];
splitViewController.tabBarItem = controller.tabBarItem;
splitViewController.viewControllers = [NSArray arrayWithObjects:nav, stockDetailController, nil];
splitViewController.delegate = stockDetailController;
[controllers replaceObjectAtIndex:index withObject:splitViewController];
}
index++;
}
tabBarController.viewControllers = controllers;
}
We succeeded in having a UISplitViewController inside a UITabViewController on iPad with iOS5+.
to make a long story short: it works:
out of the box if you accept a split also in portrait;
with a bit of
work, if you want to have the master view hidden in portrait, and
have it appear only upon tapping a button.
The trick in the second case is to use the IntelligentSplitViewController (see a few posts up, thanx Greg Combs) or similarly extend a UISplitVC, and be careful that the delegate of the subclass of the splitview controller is always a live object.
We have detailed the process on:
https://devforums.apple.com/message/763572#763572

How can we change views in a UISplitViewController other than using the popover and selecting?

I have done a sample app with UISplitViewController studying the example they have provided. I have created three detailviews and have configured them to change by the default means. Either using the left/master view in landscape AND using the popover in the portrait orientation.
Now I am trying to move to another view(previous/next) from the currentView by using left/right swipe in each view. For that, what I did was just created a function in the RootViewController. I copy-pasted the same code as that of the tablerow selection used by the popover from the RootViewController. I am calling this function from my current view's controller and is passing the respective index of the view(to be displayed next) from the current view. Function is being called but nothing is happening.
Plz help me OR is anyother way to do it other than this complex step? I am giving the function that I used to change the view.
- (void) rearrangeViews:(int)viewRow
{
UIViewController <SubstitutableDetailViewController> *detailViewController = nil;
if (viewRow == 0) {
DetailViewController *newDetailViewController = [[DetailViewController alloc] initWithNibName:#"DetailView" bundle:nil];
detailViewController = newDetailViewController;
}
if (viewRow == 1) {
SecondDetailViewController *newDetailViewController = [[SecondDetailViewController alloc] initWithNibName:#"SecondDetailView" bundle:nil];
detailViewController = newDetailViewController;
}
if (viewRow == 2) {
ThirdDetailViewController *newDetailViewController = [[ThirdDetailViewController alloc] initWithNibName:#"ThirdDetailView" bundle:nil];
detailViewController = newDetailViewController;
}
// Update the split view controller's view controllers array.
NSArray *viewControllers = [[NSArray alloc] initWithObjects:self.navigationController, detailViewController, nil];
splitViewController.viewControllers = viewControllers;
[viewControllers release];
if (rootPopoverButtonItem != nil) {
[detailViewController showRootPopoverButtonItem:self.rootPopoverButtonItem];
}
[detailViewController release];
}
I have googled and found a nice approach. They use a UINavigationController on the detail view to push new views. You could use this same solution for your project.
http://www.cimgf.com/2010/05/24/fixing-the-uisplitviewcontroller-template/

Resources