split view UISplitViewController
pop over UIPopoverController
1. Storyboard 框架
The Storyboard Defines the Structure
Creating a SplitView Project
Master-Detail Application 模板
for pad,use ARC,use Storyboard
模板包含
app delegate
BIDMasterViewController
BIDDetailViewController
A split view controller that contains all the elements;
splitViewController作为stroyboard初始视图控制器
A navigation controller to handle what‘s happening on the left side of the split;
左侧有个navigation controller
A master view controller (displaying a master list of items) inside the navigation controller;
左侧的navigation controller的topViewController是master view controller
A detail view controller on the right.
右侧也是一个navigation view controller,其中有个detail view controller
UISplitViewController
@implementation BIDAppDelegate
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
//下面代码只是用来设置stroyboard中,无法设置的sense与sense之间对象的关联
// Override point for customization after application launch.
UISplitViewController *splitViewController = (UISplitViewController *)self.window.rootViewController;
//有两个navigationcontroller,left and right,此时获取的是right,detailViewController
UINavigationController *navigationController = [splitViewController.viewControllers lastObject];
//设置splitViewController的委托,用来在detail view controller中响应左侧视图显示隐藏
//设置委托,用于响应横竖屏幕下,popOverController的显示和消失
splitViewController.delegate = (id)navigationController.topViewController; //navigationController的顶层视图控制器
return YES;
}
@end
为什么不直接在stroyboard中设置此委托?
a complex storyboard is not normally loaded all at once。
Since Interface Builder has no way of knowing which scenes will coexist, it actually forbids you from
making any outlet or target/action connections from an object in one scene to an object in another
scene. In fact, the only connections it allows you to make from one scene to another are segues.
storyborad中的sence不是一起加载的,而是按需加载。
正因为如此,IB不知道哪些sence会同时存在,
所以它禁止关联任何outlet或者target/action,在不同sence的对象之间。
实际上,唯一能在不同sence之间关联的,只有segue
所以我们必须在代码中,手动连接不同sence之间的对象。
The Master View Controller
@class BIDDetailViewController;
@interface BIDMasterViewController : UITableViewController
@property (strong, nonatomic) BIDDetailViewController *detailViewController;
@end
- (void)awakeFromNib
{
self.clearsSelectionOnViewWillAppear = NO; //再次显示,保存单元的选中
self.contentSizeForViewInPopover = CGSizeMake(320.0, 600.0);
[super awakeFromNib];
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.navigationItem.leftBarButtonItem = self.editButtonItem; //前面讲过,每个viewController都有此按钮,用来翻转编辑状态
self.navigationItem.rightBarButtonItem =
[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd
target:self
action:@selector(insertNewObject:)];
//不就是前面设置的委托?
self.detailViewController = (BIDDetailViewController *)[[self.splitViewController.viewControllers lastObject] topViewController];
}
The Detail View Controller
@interface BIDDetailViewController ()
@property (strong, nonatomic) UIPopoverController *masterPopoverController;
- (void)configureView;
@end
//Creating a class extension lets you define some methods and properties that are going to be used
//within your class but that you don’t want to expose to other classes in a header file.
//使用名称为空的扩展,定义私有的属性和方法
//如果只是定义实例变量,则可直接在{ }中
@implementation BIDDetailViewController
#pragma mark - Managing the detail item
- (void)setDetailItem:(id)newDetailItem
{
if (_detailItem != newDetailItem)//strong对象,所以能直接比较对象地址,来区分是否为同一个
{
_detailItem = newDetailItem;[self configureView];
}
if (self.masterPopoverController != nil)
{
[self.masterPopoverController dismissPopover Animated: YES];//竖向下,masterPopoverController非空,选择不同的object后,让popOver立即消失
}
}
- (void)configureView
{
if (self.detailItem)
{
self.detailDescriptionLabel.text = [self.detailItem description];
}
}
- (void)viewDidLoad
{
[super viewDidLoad];
[self configureView];
}
#pragma mark - Split view 委托方法(ios8中已经干掉此委托方法)
It‘s called when the split view controller is no longer going to show the left side of the split view as a permanent fixture.
当左侧不再固定显示的时候,调用此方法。即ipad从横屏转为竖屏时。
- (void)splitViewController:(UISplitViewController *)splitController
willHideViewController:(UIViewController *)viewController //left view controller
withBarButtonItem:(UIBarButtonItem *)barButtonItem
forPopoverController:(UIPopoverController *)popoverController
{
barButtonItem.title = NSLocalizedString(@"Master", @"Master");
[self.navigationItem setLeftBarButtonItem:barButtonItem animated:YES];
self.masterPopoverController = popoverController;
}
连贯起来是,splitViewController将隐藏左侧masterViewController
但是通过barButtonItem,可以激活popoverController,来显示masterViewController
//the user switches back to landscape orientation
//从竖向,切换回横屏,popover机制消失,将会固定显示masterViewController
- (void)splitViewController:(UISplitViewController *)splitController
willShowViewController:(UIViewController *)viewController //left view controller
invalidatingBarButtonItem:(UIBarButtonItem *)barButtonItem //使得其无效
{
[self.navigationItem setLeftBarButtonItem:nil animated:YES];
self.masterPopoverController = nil;
}
@end
UIWebView
NSURL *url = [NSURL URLWithString: self.detailItem];
NSURLRequest *request = [NSURLRequest requestWithURL: url];
[self.webView loadRequest: request];
2. 自定义popover
Creating Your Own Popover
BIDLanguageListController: UITableViewController
@interface BIDLanguageListController : UITableViewController
@property (weak, nonatomic) BIDDetailViewController *detailViewController;
@property (strong, nonatomic) NSArray *languageNames;
@property (strong, nonatomic) NSArray *languageCodes;
@end
?
- (void)viewDidLoad
{
[super viewDidLoad];
self.languageNames = @[@"English", @"French", @"German", @"Spanish"];
self.languageCodes = @[@"en", @"fr", @"de", @"es"];
self.clearsSelectionOnViewWillAppear = NO; //view不显示的时候,选中不清空
//在popover中显示的大小
self.contentSizeForViewInPopover = CGSizeMake(320.0, [self.languageCodes count] * 44.0);
[self.tableView registerClass:[UITableViewCell class]
forCellReuseIdentifier:@"Cell"];
}
#import <UIKit/UIKit.h>
@interface BIDDetailViewController : UIViewController <UISplitViewControllerDelegate>
@property (strong, nonatomic) id detailItem;
@property (weak, nonatomic) IBOutlet UILabel *detailDescriptionLabel;
@property (weak, nonatomic) IBOutlet UIWebView *webView;
@property (strong, nonatomic) UIBarButtonItem *languageButton;
@property (strong, nonatomic) UIPopoverController *languagePopoverController;
@property (copy, nonatomic) NSString *languageString;
- (IBAction)toggleLanguagePopover;
@end
- (void)viewDidLoad
{
[super viewDidLoad];
self.languageButton = [[UIBarButtonItem alloc] initWithTitle:@"Choose Language"
style:UIBarButtonItemStyleBordered
target:self
action:@selector(toggleLanguagePopover)];
self.navigationItem.rightBarButtonItem = self.languageButton;
[self configureView];
}
- (IBAction)toggleLanguagePopover
{
if (self.languagePopoverController == nil)
{
BIDLanguageListController *languageListController = [[BIDLanguageListController alloc] init];languageListController.detailViewController = self;
UIPopoverController *poc = [[UIPopoverController alloc]
initWithContentViewController: languageListController];
[poc presentPopoverFromBarButtonItem: self.languageButton
permittedArrowDirections: UIPopoverArrowDirectionAny
animated:YES];
self.languagePopoverController = poc;
}
else
{
if (self.languagePopoverController != nil){
[self.languagePopoverController dismissPopoverAnimated:YES];self.languagePopoverController = nil;
}
}
}
总结:
1. MasterDetail模版,提供了一个框架
支持splitView
横屏下,master固定在左侧,通过tableview显示数据
竖屏下,master被隐藏,不过可以通过popover显示
右侧detail显示master中选中对象的具体细节
注意1:
此模版中,从splitViewController到navigationController,到masterViewController,再到detailViewController
相互之间,都可以取到对方的值
app.window.rootViewController == splitViewController
splitViewController.viewControllers == @[masterNavigation, detailNavigation];
masterNavigation.topViewController == masterViewController
detailNavigation.topViewController == detailViewController
masterViewController和detailViewController,又分别知道其navigator
masterViewController和detailViewController, 还知道其splitViewController
Controller之间的关系都是相互的,只有各strong和weak的区别
比如在masterController中,想获取到detailController,可这么干:
detailViewController = [ [ [self.splitViewController viewControllers] lastObject] topViewController];
注意2:私有成员的定义
数据成员,以下两种方式都可以,因为有一对{}
//@interface HYDMasterViewController () {
// NSMutableArray *_objects; //私有数据成员
//}
//@end
@implementation HYDMasterViewController
{
NSMutableArray *_objects; //私有数据成员
}
但是属性和方法,不能定义在{}内,所以对于属性和方法,定义私有的话,只能采用空扩展的方法
@interface HYDDetailViewController ()
@property (strong, nonatomic) UIPopoverController *masterPopoverController;
- (void)configureView;
@end
2. 自定义popover也很简单:
1.自定义viewController,注意设置其在popOver中的大小
self.contentSizeForViewInPopover = CGSizeMake(320.0, [self.languageCodes count] * 44.0);
2.通过某个控件弹出popOver
1)即初始化viewController
2)初始化popOver,通过方法initWithContentViewController
3)设置popOver显示效果presentPopoverFromBarButtonItem
BIDLanguageListController *languageListController = [[BIDLanguageListController alloc] init];
UIPopoverController *poc = [[UIPopoverController alloc]
initWithContentViewController: languageListController];
[poc presentPopoverFromBarButtonItem: self.languageButton
permittedArrowDirections: UIPopoverArrowDirectionAny
animated:YES];