Пишем свой аналог 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
    • +18
    • 2,7k
    • 7
    Поделиться публикацией
    Похожие публикации
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

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

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

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