Search code examples
iosuitabbarcontrolleruisplitview

MGSplitViewController not as RootView but within a UIViewController


I'm very new to iOS programming (Coming from Java / C++). I'm trying to set up an app with a TabBarController of which one tab should be a SplitView. I've done my research and I know that UISplitview will not work and everywhere people recommend using the MGSplitViewController. I've looked at the demo but I just can't figure out how to use it without it beeing the app's root view and can't find any sample code that could help So here is what I do with the classes from the demo in a separate UIViewController class that I afterwards add to the TabBarController: This is my class:

#import <UIKit/UIKit.h>
#import "MGSplitCornersView.h"
#import "RootViewController.h"
#import "DetailViewController.h"



@interface ChannelViewController : UIViewController {
    MGSplitViewController *splitViewController;
    RootViewController *rootViewController;
    DetailViewController *detailViewController;

}

@property (nonatomic, retain) MGSplitViewController *splitViewController;
@property (nonatomic, retain) RootViewController *rootViewController;
@property (nonatomic, retain) DetailViewController *detailViewController;


@end

And this is my desperate try to set it up

- (id)initWithTabBar
{
    self = [super init];

    //this is the label on the tab button itself
    self.title = @"SplitView";

    //use whatever image you want and add it to your project
    //self.tabBarItem.image = [UIImage imageNamed:@"name_gray.png"];

    // set the long name shown in the navigation bar at the top
    self.navigationItem.title=@"Nav Title";

    self.splitViewController = [[MGSplitViewController alloc] init];
    self.rootViewController = [[RootViewController alloc] init];
    self.detailViewController = [[DetailViewController alloc] init];

    [self.splitViewController setDetailViewController:detailViewController];
    [self.splitViewController setMasterViewController:rootViewController];

    [self.view addSubview:splitViewController.view];

    [self.rootViewController performSelector:@selector(selectFirstRow) withObject:nil afterDelay:0];
    [self.detailViewController performSelector:@selector(configureView) withObject:nil afterDelay:0];

    if (NO) { // whether to allow dragging the divider to move the split.
    splitViewController.splitWidth = 15.0; // make it wide enough to actually drag!
    splitViewController.allowsDraggingDivider = YES;
    }

    return self;
}

I guess I'm doing something wrong with delegates? Or do I have something else mixed up? Is the demo doing things in the IB that I can't see in the code? I get the split view but no content and especially no navigation bar with the buttons the demo comes with.

I'd be very thankful for hints or sample code!


Solution

  • Ok manny, here we go. This is my working code for the interface:

    #import <UIKit/UIKit.h>
    #import "MGSplitViewController.h"
    #import "ecbView.h"
    #import "ecbCalc.h"
    
    @interface splitMain : MGSplitViewController <UIPopoverControllerDelegate,
                                                  MGSplitViewControllerDelegate>
    {
        IBOutlet UIPopoverController*       popoverController;
        IBOutlet UINavigationController*    naviController;
        IBOutlet ecbCalc*                   viewCalcLeft;
        IBOutlet ecbView*                   euroRatesRight;
                 UIBarButtonItem*           savedButtonItem;
                 BOOL                       keepMasterInPortraitMode;
                 BOOL                       memoryWasDropped;
                 BOOL                       viewLoaded;
    }
    
    @property (nonatomic, retain) UIPopoverController* popoverController;
    @property (nonatomic, retain) UINavigationController* naviController;
    @property (nonatomic, retain) ecbCalc* viewCalcLeft;
    @property (nonatomic, retain) ecbView* euroRatesRight;
    @property (nonatomic, retain) UIBarButtonItem* savedButtonItem;
    @property (nonatomic, readonly) BOOL keepMasterInPortraitMode;
    @property (nonatomic, readonly) BOOL memoryWasDropped;
    @property (nonatomic, readonly) BOOL viewLoaded;
    
    - (void)dismissPopoverController: (BOOL)animated;
    - (void)settingsChanged;
    
    @end
    

    and here excerpts from implementation file:

    - (id)initWithCoder:(NSCoder *)aDecoder
    {
        if ((self = [super initWithCoder:aDecoder]))
        {
            // my initialization...
        }
    
        return self;
    }
    
    // Implement loadView to create a view hierarchy programmatically, without using a nib.
    - (void)loadView
    {
        CGRect  rectFrame = CGRectMake(0.0, 20.0, 768.0, 1004.0 - 48.0);    // being above a tab bar!
    
        viewLoaded     = NO;
        self.view      = [[UIView alloc] initWithFrame:rectFrame];
        viewCalcLeft   = [[ecbCalc alloc] initWithNibName:@"ecbCalc" bundle:nil];
        euroRatesRight = [[ecbView alloc] initWithNibName:@"ecbView-iPad" bundle:nil];
        naviController = [[UINavigationController alloc] initWithRootViewController:self.viewCalcLeft];
        naviController.navigationBar.barStyle = UIBarStyleBlack;
        naviController.title = nil;
        viewCalcLeft.title   = NSLocalizedString(@"BtnTitleCalc",  @"");
        viewCalcLeft.view.hidden = NO;
    
        NSUserDefaults* prefs = [NSUserDefaults standardUserDefaults];
    
        if ([prefs objectForKey:@"iPadAlwaysSplitTableView"] != nil)
            self.keepMasterInPortraitMode = [prefs boolForKey:@"iPadAlwaysSplitTableView"];
        else
            self.keepMasterInPortraitMode = YES;
    
        NSArray*    theViewControllers = [NSArray arrayWithObjects:self.naviController, self.euroRatesRight, nil];
    
        [self setViewControllers:theViewControllers];
        [self setDelegate:self];
        [self setShowsMasterInPortrait:keepMasterInPortraitMode];
    }
    
    // Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
    - (void)viewDidLoad
    {
        // protection because this one is called twice
        if (viewLoaded)
            return;
    
        [super viewDidLoad];
    
        if (memoryWasDropped)
        {
            if (!self.keepMasterInPortraitMode && UIInterfaceOrientationIsPortrait(self.interfaceOrientation))
            {
                // recreate popover controller
                self.popoverController = [[UIPopoverController alloc] initWithContentViewController:self.viewCalcLeft];
            }
        }
    
        viewLoaded = YES;
    }
    
    - (void)didReceiveMemoryWarning
    {
        // Releases the view if it doesn't have a superview.
        [super didReceiveMemoryWarning];
        memoryWasDropped = YES;
    
        // Release any cached data, images, etc. that aren't in use.
    }
    
    - (void)viewDidUnload
    {
        [super viewDidUnload];
        // Release any retained subviews of the main view.
        // e.g. self.myOutlet = nil;
        [self dismissPopoverController:NO];
    
        self.popoverController = nil;
        self.naviController    = nil;
        self.viewCalcLeft      = nil;
        self.euroRatesRight    = nil;
        viewLoaded = NO;
    }
    

    My MainWindow.xib has a UITabBarController and the button for splitMain is configured for this class but with an empty xib entry. So creation has to go via loadView. Maybe I could have done the viewDidLoad stuff within loadView ... but so I had to protect viewDidLoad from being called twice. That happens in loadView as soon as the view is instantiated from MGSplitViewController class because the initWithCoder there is calling [self setup]. In that function the frame rect is calculated with self.view.bounds so that viewDidLoad is called again because the view doesn't exist yet. Maybe one could implement a workaround within MGSplitViewController.m but I was too lazy doing that.

    To get this working on a tab bar controller please make sure you commit most of the changes that are published on the MGSplitViewController's git page. Good luck.