UINavigationController

UINavigationController

當界面交互是:列表-->明細-->更具體的明細,這種情況就需要用UINavigationController

UINavigationController維護a stack of those screens,每一個screen都是UIViewController的view,stack是一個view controller數組。

當創建UINavigationController實例時,為其指定一個UIViewController,這個view controller就是navigation controller's root view controller,其總在stack的最下面。

和UITabBarController一樣,UINavigationController也有一個viewControllers數組,root view controller是這個數組的第一個對象,當更多的view controller push到stack時,他們被添加到這個數組中,數組的最后一個對象總是在stack的頂部,UINavigationController's topViewController屬性指向棧頂的view controller。

UINavigationControllerUIViewController的子類,所以他也有一個view屬性,其view有兩個subview:UINavigationBar和topViewController

將navigation controller設置為窗口的rootViewController

#import "BKAppDelegate.h"
#import "BKItemsViewController.h"

@implementation BKAppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    // Override point for customization after application launch.
   
    BKItemsViewController *itemsViewController = [[BKItemsViewController alloc] init];
    // Create an instance of UINavigationController
    UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:itemsViewController];
    self.window.rootViewController = navController;
    
    
    self.window.backgroundColor = [UIColor whiteColor];
    [self.window makeKeyAndVisible];
    return YES;
}

@end

創建BKDetailViewController

創建BKDetailViewController類,繼承UIViewController類,同時創建XIB文件。


打開assistant editor

在project navigator,Option-click BKDetailViewController.m文件,打開assistant editor

simulated metrics

選擇root view,然后打開attributes inspector,在Simulated Metrics section為top bar選擇Translucent Navigation Bar,可以模擬navigation bar的顯示


將XIB中的視圖outlet到 view controller

Control-drag UITextField到view controller的class extension中。


將view controller放入navigation controller's stack

navigation controller's stack中的view controller,都可以通過navigationController方法來獲得navigation controller.

root view controller創建下一個view controller,并把他放入到navigation controller stack中,然這個view controller創建下一個需要入棧的view controller,以此類推。

// 選中一行
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
    BKDetailViewController *detailViewController = [[BKDetailViewController alloc] init];
    [self.navigationController pushViewController:detailViewController animated:YES];
}

在view controllers之間傳遞數據

Having all of the data in the root view controller and passing subsets of that data to the next UIViewController is a clean and efficient way of performing this task.

為了讓BKDetailViewController顯示BKItem的各屬性值,需要將BKItem傳遞給detail view controller.

在BKDetailViewController頭文件中聲明BKItem屬性:

#import <UIKit/UIKit.h>

@class BKItem;

@interface BKDetailViewController : UIViewController

@property (nonatomic,strong) BKItem *item;

@end

當BKDetailViewController‘s view顯示到屏幕上之前,將BKItem的值設置到其view上,所以在viewWillAppear:中設置

#import "BKDetailViewController.h"
#import "BKItem.h"

@interface BKDetailViewController ()
@property (weak, nonatomic) IBOutlet UITextField *nameField;
@property (weak, nonatomic) IBOutlet UITextField *serialField;
@property (weak, nonatomic) IBOutlet UITextField *valueField;
@property (weak, nonatomic) IBOutlet UILabel *dateLabel;

@end

@implementation BKDetailViewController

- (void)viewWillAppear:(BOOL)animated{
    [super viewWillAppear:animated];
    
    BKItem *item = self.item;
    
    self.nameField.text = item.itemName;
    self.serialField.text = item.serialNumber;
    self.valueField.text = [NSString stringWithFormat:@"%d", item.valueInDollars];
    
    static NSDateFormatter *dateFormatter = nil;
    if(!dateFormatter){
        dateFormatter = [[NSDateFormatter alloc] init];
        dateFormatter.dateStyle = NSDateFormatterMediumStyle;
        dateFormatter.timeStyle = NSDateFormatterNoStyle;
    }
    self.dateLabel.text = [dateFormatter stringFromDate:item.dateCreated];
}

@end

更新BKItemsViewController.m,為BKDetailViewController傳值:

// 選中一行
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
    BKDetailViewController *detailViewController = [[BKDetailViewController alloc] init];
    
    NSArray *items = [[BKItemStore sharedStore] allItems];
    BKItem *selectedItem = items[indexPath.row];
    
    detailViewController.item = selectedItem;
        
    [self.navigationController pushViewController:detailViewController animated:YES];
}

Appearing and disappearing views

當navigation controller切換視圖時,會分別發送viewWillDisappear:viewWillAppear:到將消失和將顯示的view controller

When the message endEditing: is sent to a view, if it or any of its subviews is currently the first responder, it will resign its first responder status, and the keyboard will be dismissed.

當BKDetailViewController的視圖將要消失時,更新BKItem:

// 在視圖要消失前,更新BKItem
- (void)viewWillDisappear:(BOOL)animated{
    
    [super viewWillDisappear:animated];
    [self.view endEditing:YES];
    
    BKItem *item = self.item;
    item.itemName = self.nameField.text;
    item.serialNumber = self.serialField.text;
    item.valueInDollars = [self.valueField.text intValue];
}

在BKItemsViewController的視圖將要顯示時,刷新table view:

- (void)viewWillAppear:(BOOL)animated{
    [super viewWillAppear:animated];
    
    [self.tableView reloadData];
}

UINavigationBar

每一個UIViewController有一個navigationItem屬性,其類型是UINavigationItem,不像UINavigationBar,UINavigationItem不是UIView的子類,所以他不能出來顯示在屏幕上。

當一個view controller在navigation controller的棧頂時,UINavigationBar用這個view controller的navigationItem的值來配置自己。

簡單navigation item

默認,navigation item是空,最簡單的navigation item,可以只配置他的title屬性。

當一個view controller在navigation controller的棧頂時,并且其navigationItem有一個有效的title屬性,navigation bar就會顯示這個title。

修改BKItemsViewController.m,在初始化時設置navigationItem's title:

- (instancetype)init{
    self = [super initWithStyle:UITableViewStylePlain];
    
    if(self){
        // 設置其本身的navigationItem
        UINavigationItem *navItem = self.navigationItem;
        navItem.title = @"Homepwner";
    }
       
    return self;
}

當為BKDetailViewController設置BKItem時(set方法),設置其navigationItem title為BKItem's name。

- (void)setItem:(BKItem *)item{
    _item = item;
    self.navigationItem.title = _item.itemName;
}

復雜navigation item

navigation item不光可以只定義title屬性,還可以很復雜。如下圖所示,每個UINavigationItem有三個可自定義區:leftBarButtonItem,rightBarButtonItem,titleView。
前兩個是UIBarButtonItem實例,這個類型的實例只能用來配置UINavigationBar或UIToolbar中的button的顯示內容。

和UINavigationItem一樣,UIBarButtonItem也不是UIView的子類,所以不能顯示在屏幕上。

第三個titleView,可以是一個基本的title字符串,也可以是任意的UIView子類,只能任選一種。

下面將BKItemsViewController的headerView(自定義的view)替換成UINavigationBar。

navigation bar 的button,和UIControl 的target-action機制一樣,當被點擊,發送action消息到target。
在XIB中設置target-action pair,Control-drag button組件到target,并選擇IBAction list中的一個。
也可以通過代碼的方式,創建target-action pair,傳遞target和action到button.

在BKItemsViewController.m文件,創建UIBarButtonItem實例,并為其傳遞target和action。

- (instancetype)init{
    self = [super initWithStyle:UITableViewStylePlain];
    
    if(self){
        // 設置其本身的navigationItem
        UINavigationItem *navItem = self.navigationItem;
        navItem.title = @"Homepwner";
        
        // 創建一個bar button item,并設置target action
        UIBarButtonItem *bbi = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(addNewItem:)];
        // 將bar button item設置給navigation item
        navItem.rightBarButtonItem = bbi;
        
        navItem.leftBarButtonItem = self.editButtonItem;
    }
       
    return self;
}

action傳遞的是一個SEL類型,這個類型指定一個方法(就是selector),@selector()不關心返回類型,參數類型或參數名稱。

UIViewController有一個editButtonItem屬性,當調用這個屬性,view controller創建一個title為Edit的UIBarButtonItem,并且已經設置好了target-action,當被點擊時,發送setEditing:animated:消息到UIViewController。

現在可以把自定義的headerView刪除了。


本文是對《iOS Programming The Big Nerd Ranch Guide 4th Edition》第十章的總結。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容