iOS5编程--官方例子代码的研究--4.NavBar
关于导航模式的view controller,这个例子是一个很有用的。下面是对这个例子的详细分析。
1. MainViewController类
程序运行的第一个界面如下:
一般情况下,导航模式的工程模板会隐含生成一个table view controller,亦即导航的主view是一个tableview,当然这个不是必须的,如果你希望生成一个主view不是tableview的也是可以的,你必须自己使用代码写,本文的后面会提到这个。
在XCode4.2中,如果你希望生成导航模式的,是选择Master-Detail Application,在下一个界面选择为iphone生成的。在XCode4.2以前的版本是选择Navigation-based Application,这里是只有为iphone生成的,不能选择为ipad生成的。
上面的第一个界面的代码是在MainViewController.h中,如果你不知道这个是如何联系起来的,请看下图,具体细节不解释了,在前面的文章中有提到的。
每一个导航模式中的view隐含的有一个导航区,就是view的上部,这里有四个部分,分别是本导航页的右button,back button, title, 左button,其中左button的位置和back button是重叠的,在导航的第一个view上是没有back button的,后面的view缺省有back button,当然,你可以使用代码隐藏这个back button.在任何界面上,缺省都没有左button,右button,和title,你需要自己手动设置。
在主view上,我们可以看到,左button是通过xib加入的。右button是通过代码加入的。
确切的说,左右和back button都不是一个普通的button,是一个button item,简化过的。
我们先看看左button的响应函数。
- (IBAction)styleAction:(id)sender
{
UIActionSheet *styleAlert = [[UIActionSheetalloc] initWithTitle:@"Choose a UIBarStyle:"
delegate:selfcancelButtonTitle:@"Cancel"
destructiveButtonTitle:nil
otherButtonTitles:@"Default",
@"BlackOpaque",
@"BlackTranslucent",
nil,
nil];
// use the same style as the nav bar
styleAlert.actionSheetStyle =self.navigationController.navigationBar.barStyle;
[styleAlertshowInView:self.view];
[styleAlertrelease];
}
这里点中左button后的界面如下:
关于Action Sheet的用法这里不再分析,你可以参考我在UICatalog中的分析。
这个是选择导航栏的样式,共有三种,你可以每一个选择一下试试。这里需要说明的地方是,Xcode会在
styleAlert.actionSheetStyle =self.navigationController.navigationBar.barStyle;
这一行上面给出一个warning 或者error(取决你的设置,但是都不影响程序的运行),因为action sheet的actionSheetStyle和导航的barStyle不是同一个定义,它们是不同的枚举类型,大家知道枚举都是从0开始编码的整数,而且都是有三种类型,所以可以直接使用等号,但是会有一个警告。在action sheet 的响应函数
- (void)actionSheet:(UIActionSheet *)modalView clickedButtonAtIndex:(NSInteger)buttonIndex
中,我们需要注意的是类似这样的代码。[UIApplicationsharedApplication].statusBarStyle =UIStatusBarStyleDefault;
这个代码是使系统状态栏的样式和导航具有相同的样式。下面我们开始分析viewDidLoad函数,
- (void)viewDidLoad
{
// Make the title of this page the same as the title of this app
self.title = [[[NSBundle mainBundle]infoDictionary] objectForKey:@"CFBundleName"];
上面的这行代码就是设置导航区的title,你可以自定义为一个view,这样可以放一个图片在上面。
self.menuList = [NSMutableArray array];
// We will lazily create our view controllers as the user requests them (at a later time),
// but for now we will encase each title an explanation text into a NSDictionary and add it to a mutable array.
// This dictionary will be used by our table view data source to populate the text in each cell.
//
// When it comes time to create the corresponding view controller we will replace each NSDictionary.
//
// If you want to add more pages, simply call "addObject" on "menuList"
// with an additional NSDictionary. Note we use NSLocalizedString to load a localized version of its title.
if (!pageNames)
{
pageNames = [[NSArrayalloc] initWithObjects:@"PageOne",@"PageTwo", @"PageThree",@"PageFour", @"PageFive",nil];
}
这里注意很多人在使用initWithObjects初始化一个NSArray的时候,会和initWithObject搞混。没有s的表示使用一个指针初始化NSArray,初始化后,这个NSArray的实例只有一个数据,所以在调用没有s的函数的时候是不能以nil结束的,但是有s的就不一样了,哪怕只有一个数据,或者没有数据,必须使用一个nil结束。
for (NSString *pageName in pageNames)
{
[self.menuListaddObject:[NSMutableDictionarydictionaryWithObjectsAndKeys:
NSLocalizedString([pageName stringByAppendingString:@"Title"], @""), kTitleKey,
NSLocalizedString([pageName stringByAppendingString:@"Explain"], @""), kDetailKey,
nil]];
}
上面的代码使用了for in又叫for each循环,如果有问题,请参考objective-c部分的说明。
上面的代码循环部分就不解释了,你可能看起来有一些吃力,请参考我前面的文章关于国际化的部分,然后打开Localizable.strings这个文件,你一目了然了。
// Create a final modal view controller
UIButton* modalViewButton = [UIButtonbuttonWithType:UIButtonTypeInfoLight];
[modalViewButtonaddTarget:selfaction:@selector(modalViewAction:)forControlEvents:UIControlEventTouchUpInside];
上面生成一个UIButton的实例,作为导航区的右button使用。
UIBarButtonItem *modalBarButtonItem = [[UIBarButtonItemalloc] initWithCustomView:modalViewButton];
self.navigationItem.rightBarButtonItem = modalBarButtonItem;
[modalBarButtonItemrelease];
这里就是使用代码添加右button的方法,很简单,但是记得需要调用[modalBarButtonItemrelease]释放UIBarButtonItem的实例,你可能会问,为何不释放UIButton的实例,因为创建的方式不同,modalBarButtonItem是使用alloc的方式,表示你自己管理内存,modalViewButton使用buttonWithType的方式,没有调用alloc,所以不需要自己释放,其实它是在自动内存释放池中。
[self.myTableViewreloadData];
}
下面一个要分析的函数是- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
这个函数在table view上一行被点中的时候调用。
NSMutableDictionary *rowData = [self.menuListobjectAtIndex:indexPath.row];
UIViewController *targetViewController = [rowDataobjectForKey:kViewControllerKey];
if (!targetViewController)
{
// The view controller has not been created yet, create it and set it to our menuList array
NSString *viewControllerName = [[pageNamesobjectAtIndex:indexPath.row]stringByAppendingString:@"ViewController"];
得到目标viewcontroller的名称
targetViewController = [[NSClassFromString(viewControllerName)alloc] initWithNibName:viewControllerNamebundle:nil];
这里是Objective-C的运行期(Run Time)的一个特性,你可以通过一个类名来生成一个类。
[rowData setValue:targetViewController forKey:kViewControllerKey];
[targetViewControllerrelease];
}
[self.navigationControllerpushViewController:targetViewController animated:YES];
导航到某个view controller,导航是一个栈的方式,所以到下一级就是push的方式。
最后分析的函数是
- (IBAction)modalViewAction:(id)sender
这个函数如下:
if (self.myModalViewController == nil)
self.myModalViewController = [[[ModalViewControlleralloc] initWithNibName:
NSStringFromClass([ModalViewControllerclass]) bundle:nil]autorelease];
[self.navigationControllerpresentModalViewController:self.myModalViewControlleranimated:YES];
presentModalViewController:animated:是使一个viewcontroller从下面升上来。2.PageOneViewController
- (void)viewDidLoad
{
// Add our custom add button as the nav bar's custom right view
UIBarButtonItem *addButton = [[[UIBarButtonItemalloc] initWithTitle:NSLocalizedString(@"AddTitle",@"")
style:UIBarButtonItemStyleBordered
target:self
action:@selector(addAction:)]autorelease];
self.navigationItem.rightBarButtonItem = addButton;
加入一个右button
}
3.PageTwoViewContrller
- (void)viewDidLoad
{
// add our custom image button as the nav bar's custom right view
UIBarButtonItem *addButton = [[UIBarButtonItemalloc] initWithImage:[UIImageimageNamed:@"email.png"]
style:UIBarButtonItemStyleBorderedtarget:selfaction:@selector(action:)];
self.navigationItem.rightBarButtonItem = addButton;
[addButtonrelease];
加入一个右button,和第一个不同的是,使用一个png图片来作为button的显示内容。
}
4.PageThreeViewController
- (void)viewDidLoad
{
// "Segmented" control to the right
UISegmentedControl *segmentedControl = [[UISegmentedControlalloc] initWithItems:
[NSArrayarrayWithObjects:
[UIImageimageNamed:@"up.png"],
[UIImageimageNamed:@"down.png"],
nil]];
[segmentedControladdTarget:selfaction:@selector(segmentAction:)forControlEvents:UIControlEventValueChanged];
segmentedControl.frame =CGRectMake(0, 0, 90, kCustomButtonHeight);
segmentedControl.segmentedControlStyle =UISegmentedControlStyleBar;
segmentedControl.momentary =YES;
defaultTintColor = [segmentedControl.tintColorretain]; // keep track of this for later
UIBarButtonItem *segmentBarItem = [[UIBarButtonItemalloc] initWithCustomView:segmentedControl];
[segmentedControlrelease];
self.navigationItem.rightBarButtonItem = segmentBarItem;
[segmentBarItem release];
上面的代码初始化右button,使用一个segmented controller,这里有两个部分,使用两个图片。很有趣,有时候我们会加入两个button,但是这个代码更简洁。关于segmented controller不是本文分析的重点,你可以在UICatalog中看到。
}
- (void)viewWillAppear:(BOOL)animated
{
UISegmentedControl *segmentedControl = (UISegmentedControl *)self.navigationItem.rightBarButtonItem.customView;
// Before we show this view make sure the segmentedControl matches the nav bar style
if (self.navigationController.navigationBar.barStyle == UIBarStyleBlackTranslucent ||
self.navigationController.navigationBar.barStyle == UIBarStyleBlackOpaque)
segmentedControl.tintColor = [UIColordarkGrayColor];
else
segmentedControl.tintColor =defaultTintColor;
是的segmented controller的背景颜色合适。
}
5.PageFourViewController
6. PageFiveViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
if (!(self = [superinitWithNibName:nibNameOrNil bundle:nibBundleOrNil]));
上面这个地方有一个编译问题,有两种方式解决,一个就是加入一个return nil;,另一个就是不是用if.
self.title =NSLocalizedString(@"PageFiveTitle",@"");
self.navigationItem.prompt =NSLocalizedString(@"Please select the appropriate media type:",@"Page Five Prompt");
使得导航区变得更宽,多显示一个文本区。
returnself;
}
7.个性化标题栏
UIImageView *fancyImageTitleView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"1.png"]];
self.navigationItem.titleView = fancyImageTitleView;
[fancyImageTitleView release];
8.使用代码生成一个导航模式的view controller.
tcMasterViewController *masterViewController = [[[tcMasterViewController alloc] initWithNibName:@"tcMasterViewController" bundle:nil] autorelease];
self.navigationController = [[[UINavigationController alloc] initWithRootViewController:masterViewController] autorelease];
self.window.rootViewController = self.navigationController;