Pull to refresh

Пишем свой аналог UISplitViewController

Reading time6 min
Views3.2K
UISplitViewController получился отличной и красивой штукой, но имеет один существенный недостаток: «The split view controller’s view should always be installed as the root view of your application window. You should never present a split view inside of a navigation or tab bar interface.» Вьюшка UISplitViewController'а всегда должна быть главной вьюшкой приложения, поэтому нельзя впихнуть невпихуемое — UISplitViewController в navigation или tab bar.


Посему, напишем свой аналог UISplitViewController'а с блекджеком и шлюхами для использования совместно с navigation или tab bar.
Создадим Window-based application и обзовём его ipad_split_view.

Создадим класс MySplitViewController:
@interface MySplitViewController : UIViewController
{
  UIViewController * detailViewController;
  UIViewController * masterViewController;
  UIPopoverController * popoverController;
  
  UINavigationBar* navigationBar;
}
@property (nonatomic, retain) IBOutlet UINavigationBar* navigationBar;
@property (nonatomic, retain) IBOutlet UIViewController * detailViewController;
@property (nonatomic, retain) IBOutlet UIViewController * masterViewController;

-(void) layoutViewsToInterfaceOrientation:(UIInterfaceOrientation)newInterfaceOrientation;
-(id) initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil;
-(IBAction) showRootPopup: (id) sender;

@end


* This source code was highlighted with Source Code Highlighter.

В реализацию добавим парочку дефайнов:
#define BAR_HEIGHT 49.f
#define SCREEN_HEIGHT 1024.f
#define SCREEN_WIDTH 768.f
#define MASTER_WIDTH 320.f


* This source code was highlighted with Source Code Highlighter.


Переопределим методы setMasterViewController и setDetailViewController:
-(void) setDetailViewController:(UIViewController *) aDetailViewController
{
  detailViewController = [aDetailViewController retain];
  [self.view addSubview:detailViewController.view];
  [self layoutViewsToInterfaceOrientation:self.interfaceOrientation];
}

-(void) setMasterViewController:(UIViewController *) aMasterViewController
{
  masterViewController = [aMasterViewController retain];
  [self.view addSubview:masterViewController.view];
  if (popoverController)
  {
    [popoverController release];
  }
  popoverController = [[UIPopoverController alloc] initWithContentViewController:masterViewController];
  [self layoutViewsToInterfaceOrientation:self.interfaceOrientation];
}

* This source code was highlighted with Source Code Highlighter.


И создадим метод layoutViewsToInterfaceOrientation. Он-то и будет вызываться при смене ориентации:
-(void) layoutViewsToInterfaceOrientation:(UIInterfaceOrientation)newInterfaceOrientation
{
  if ((newInterfaceOrientation == UIInterfaceOrientationPortrait ||
     newInterfaceOrientation == UIInterfaceOrientationPortraitUpsideDown))
  {
    [self.view addSubview:navigationBar];
    navigationBar.frame = CGRectMake(0, 0, SCREEN_WIDTH, BAR_HEIGHT);
    [masterViewController.view removeFromSuperview];
    detailViewController.view.frame = CGRectMake(0, BAR_HEIGHT, SCREEN_WIDTH, self.view.frame.size.height-BAR_HEIGHT);    
  }
  else
  {
    [navigationBar removeFromSuperview];
    [self.view addSubview:masterViewController.view];
    masterViewController.view.frame = CGRectMake(0, 0, MASTER_WIDTH, self.view.frame.size.height);
    detailViewController.view.frame = CGRectMake(MASTER_WIDTH, 0, SCREEN_HEIGHT-MASTER_WIDTH, self.view.frame.size.height);
  }  
}

* This source code was highlighted with Source Code Highlighter.


Переопределим метод shouldAutorotateToInterfaceOrientation — пусть возвращает YES для поддержки всех ориентаций.
Также переопределим метод didRotateFromInterfaceOrientation:
-(void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
{
  [super didRotateFromInterfaceOrientation:fromInterfaceOrientation];
  if ([popoverController isPopoverVisible])
  {
    [popoverController dismissPopoverAnimated:YES];
  }
  [masterViewController didRotateFromInterfaceOrientation:fromInterfaceOrientation];
  [detailViewController didRotateFromInterfaceOrientation:fromInterfaceOrientation];
  [self layoutViewsToInterfaceOrientation:self.interfaceOrientation];
}

* This source code was highlighted with Source Code Highlighter.


Создадим метод showRootPopup — он будет вызываться при нажатии на кпопку «More» в портретной ориентации:
-(IBAction) showRootPopup: (id) sender
{
  [popoverController setContentViewController:masterViewController];
  [popoverController presentPopoverFromBarButtonItem:sender
               permittedArrowDirections:UIPopoverArrowDirectionUp
                       animated:YES];
}

* This source code was highlighted with Source Code Highlighter.


Создадим для этого класса XIB и свяжем элементы интерфейса и события — это достаточно тривиально и объяснения не требует.

Далее, создадим класс RootViewController:
@interface RootViewController : UITableViewController<UITableViewDelegate, UITableViewDataSource>
{
  DetailViewController *detailViewController;
  NSArray * someArray;
}

@property (nonatomic, retain) IBOutlet DetailViewController *detailViewController;

@end

* This source code was highlighted with Source Code Highlighter.


В реализации этого класса всё очень просто и обыденно. Интерес представляет лишь следующий код:
- (void)tableView:(UITableView *)aTableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
  
  /*
   When a row is selected, set the detail view controller's detail item to the item associated with the selected row.
   */
  detailViewController.detailItem = [someArray objectAtIndex:indexPath.row];
}


* This source code was highlighted with Source Code Highlighter.

Также создадим для этого класса XIB.

Перейдём к созданию класса DetailViewController:
@interface DetailViewController : UIViewController

  id detailItem;
  UIWebView *webView;
}

@property (nonatomic, retain) id detailItem;
@property (nonatomic, retain) IBOutlet UIWebView *webView;;

@end

* This source code was highlighted with Source Code Highlighter.


В реализации обратим внимание на 2 метода:
- (void)setDetailItem:(id)newDetailItem
{
  if (detailItem != newDetailItem)
  {
    [detailItem release];
    detailItem = [newDetailItem retain];
    
    // Update the view.
    [self configureView];
  }
}

- (void)configureView {
  // Update the user interface for the detail item.
  NSURL * url          = [NSURL URLWithString:detailItem];
  NSURLRequest * urlRequest  = [NSURLRequest requestWithURL:url];
  [webView loadRequest:urlRequest];
}

* This source code was highlighted with Source Code Highlighter.


Как обычно, создаём XIB и связи в нём.

Осталось лишь свзять всё написанное. Класс ipad_split_viewAppDelegate будет выглядеть так:
@class MySplitViewController;

@interface ipad_split_viewAppDelegate : NSObject <UIApplicationDelegate>
{
  UIWindow *window;
  MySplitViewController *splitViewController;
}

@property (nonatomic, retain) IBOutlet UIWindow *window;
@property (nonatomic, retain) IBOutlet MySplitViewController *splitViewController;

@end

* This source code was highlighted with Source Code Highlighter.


В реализации переопределим инициализацию:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{   
  RootViewController *rootViewController    = [[[RootViewController alloc] initWithNibName:@"RootViewController" bundle:nil] autorelease];
  DetailViewController *detailViewController  = [[[DetailViewController alloc] initWithNibName:@"DetailViewController" bundle:nil] autorelease];
  
  rootViewController.detailViewController = detailViewController;
  
  splitViewController.masterViewController = rootViewController;
  splitViewController.detailViewController = detailViewController;
  
  [window addSubview:splitViewController.view];
  [window makeKeyAndVisible];
  
  return YES;
}

* This source code was highlighted with Source Code Highlighter.


Остаётся лишь в MainWindow.xib связать элементы интерфейса.

Маленький совет. Если класс недоступен для выбора в Interface Builder, просто перетащите файл заголовка на окно Document.

Исходный код можно скачать здесь
Конечный вид нашего самодельного split view:image
image
Tags:
Hubs:
Total votes 34: ↑26 and ↓8+18
Comments7

Articles