6 мая 2010 в 16:39

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

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
Anton Baterikov @btn
карма
28,0
рейтинг 0,0
Похожие публикации
Самое читаемое Разработка

Комментарии (7)

  • +7
    Ну работа хороша. Все ОК. Но блин. Не просто так же Вам написали о его использовании. А Вы все рушите.
    Нет. Я за стандартизацию интерфейсов. Что бы все было логично и сразу понятно.
    • 0
      я с вами согласен, но бывают такие моменты, когда заказчик прав, возможно это и послужило причиной написания этого
      автору спасибо за код
      • НЛО прилетело и опубликовало эту надпись здесь
        • 0
          тут вроде ж гайлайны не нарушаются. поповеры использовать можно отдельно.
        • 0
          не всегда «заказчик- не разработчик». К примеру, я имел дело с заказчиком, который одновременно был и разработчиком. Это во-первых. Ну а во-вторых, заказчик бывает разный… и черный, и белый, и красный. Кто-то поймет объяснения, а кто-то скажет «хочу так и точка».
          Да и сравнение клиентов с бабами немного не уместно.
          • НЛО прилетело и опубликовало эту надпись здесь
  • 0
    да, возможно я и являюсь «приставкой к клавиатуре»… с чего-то надо начинать(Не всем удается с самого рождения быть авторитетом. авторитетом для клиента.)

    Ну а женщины. Я, наверное, общался с другим типом женщин.

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.