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:
В реализацию добавим парочку дефайнов:
Переопределим методы setMasterViewController и setDetailViewController:
И создадим метод layoutViewsToInterfaceOrientation. Он-то и будет вызываться при смене ориентации:
Переопределим метод shouldAutorotateToInterfaceOrientation — пусть возвращает YES для поддержки всех ориентаций.
Также переопределим метод didRotateFromInterfaceOrientation:
Создадим метод showRootPopup — он будет вызываться при нажатии на кпопку «More» в портретной ориентации:
Создадим для этого класса XIB и свяжем элементы интерфейса и события — это достаточно тривиально и объяснения не требует.
Далее, создадим класс RootViewController:
В реализации этого класса всё очень просто и обыденно. Интерес представляет лишь следующий код:
Также создадим для этого класса XIB.
Перейдём к созданию класса DetailViewController:
В реализации обратим внимание на 2 метода:
Как обычно, создаём XIB и связи в нём.
Осталось лишь свзять всё написанное. Класс ipad_split_viewAppDelegate будет выглядеть так:
В реализации переопределим инициализацию:
Остаётся лишь в MainWindow.xib связать элементы интерфейса.
Маленький совет. Если класс недоступен для выбора в Interface Builder, просто перетащите файл заголовка на окно Document.
Исходный код можно скачать здесь
Конечный вид нашего самодельного split view:
Посему, напишем свой аналог UISplitViewController'а
Создадим 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: