No4 私人通訊錄

一 搭建界面

  • LoginViewController
// 設置賬號文本框的代理,不能及時監聽文本框的內容改變
   _accountField.delegate = self;
/ 攔截用戶的輸入,每次用戶想要修改文本框的內容的時候就會調用
// 這個方法不能及時監聽文本框的內容改變
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
    return YES;
}

注意:上述方法不能及時監聽文本框內容改變,只能攔截用戶輸入
如果需要及時監聽文本框內容改變,用下述方法:

- (void)viewDidLoad {
    [super viewDidLoad];

    // 及時監聽文本框的內容改變
    [_accountField addTarget:self action:@selector(textChange) forControlEvents:UIControlEventEditingChanged];
    [_pwdField addTarget:self action:@selector(textChange) forControlEvents:UIControlEventEditingChanged];
}
  • 登陸邏輯
// 賬號或者密碼文本框改變的時候都會調用
- (void)textChange
{
    // 同時有內容,允許登錄按鈕點擊
    _loginBtn.enabled = _accountField.text.length && _pwdField.text.length;
}

二 自動登陸邏輯實現

  • 自動登陸 = YES時,打開記住密碼
// 當自動登錄開關狀態改變的時候調用
- (IBAction)autoLoginChange:(UISwitch *)sender {
    // 當勾選自定登錄,必須勾選記住密碼

    // 判斷下自動登錄開關是否打開
    if (sender.on == YES) { // 打開自動登錄
        // 打開記住密碼
        [_rmbPwdSwitch setOn:YES animated:YES];
    }
}
  • 記住密碼 = NO時,自動登錄關閉
// 當記住密碼開關狀態改變的時候調用
- (IBAction)rmbPwdChange:(UISwitch *)sender {

    // 當記住密碼關閉,把自動登錄關閉
    if (sender.on == NO) { // 關閉記住密碼
        [_autoLoginSwitch setOn:NO animated:YES];
    }    
}

三 Segue

  • 什麼是Segue
    • Storyboard上每一根用來界面跳轉的線,都是一個UIStoryboardSegue對象(簡稱Segue)
  • Segue的屬性
    • 每一個Segue對象,都有3個屬性
// 唯一標識
@property (nonatomic, readonly) NSString *identifier;
// 來源控制器
@property (nonatomic, readonly) id sourceViewController;
// 目標控制器
@property (nonatomic, readonly) id destinationViewController;

這裏寫圖片描述

  • Segue的類型

    • 根據Segue的執行(跳轉)時刻,Segue可以分爲2大類型
      • 自動型:點擊某個控件後(比如按鈕),自動執行Segue,自動完成界面跳轉
      • 手動型:需要通過寫代碼手動執行Segue,才能完成界面跳轉
  • 自動型Segue

    • 按住Control鍵,直接從控件拖線到目標控制器
    • 點擊“登錄”按鈕後,就會自動跳轉到右邊的控制器
    • 如果點擊某個控件後,不需要做任何判斷,一定要跳轉到下一個界面,建議使用“自動型Segue”
  • 手動型Segue

    • 按住Control鍵,從來源控制器拖線到目標控制器
    • 手動型的Segue需要設置一個標識
    • 在恰當的時刻,使用perform方法執行對應的Segue

      [self performSegueWithIdentifier:@"login2contacts" sender:nil];
      // Segue必須由來源控制器來執行,也就是說,這個perform方法必須由來源控制器來調用
      
    • 如果點擊某個控件後,需要做一些判斷,也就是說:滿足一定條件後才跳轉到下一個界面,建議使用“手動型Segue”

  • Sender參數的傳遞

[self performSegueWithIdentifier:@“login2contacts” sender:@“jack”];

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender;
  • 登陸跳轉實現
- (IBAction)login:(id)sender {
    // 默認網絡延遲,目的不要這麼快就跳轉 
    // 彈出蒙版
    [MBProgressHUD showMessage:@"正在登錄ing..."];
    // 1.延遲判斷

    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{

        // 隱藏蒙版
        [MBProgressHUD hideHUD];

        // 判斷下賬號和密碼是否正確
        if ([_accountField.text isEqualToString:@"xmg"] && [_pwdField.text isEqualToString:@"123"]) {

            // 輸入正確,直接進入聯繫人界面
            // 跳轉到下一個控制器
            // 手動執行segue進行跳轉
            [self performSegueWithIdentifier:@"login2Contact" sender:nil];

        }else{
            // 輸入錯誤,提示用戶賬號或者密碼錯誤
            [MBProgressHUD showError:@"用戶賬號或者密碼錯誤"];
        }
    });    
}

四 數據傳值-順傳

  • 順傳:來源控制器傳值給目的控制器,上一個控制器傳值給下一個控制器

  • 數據傳值步驟

    • 1.首先接收方要有一個屬性接收數據
    • 2.必須要在傳遞方拿到接收方,給它傳遞數據
  • 利用performSegueWithIdentifier:方法可以執行某個Segue,完成界面跳轉

    • 1>根據identifier去storyboard中找到對應的線,新建UIStoryboardSegue對象
      • 設置Segue對象的sourceViewController(來源控制器)
      • 新建並且設置Segue對象的destinationViewController(目標控制器)
    • 2>調用sourceViewController的下面方法,做一些跳轉前的準備工作並且傳入創建好的Segue對象

      - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender;
      // 這個sender是當初performSegueWithIdentifier:sender:中傳入的sender
      
    • 3>調用Segue對象的- (void)perform;方法開始執行界面跳轉操作

      • 如果segue的style是push
        • 取得sourceViewController所在的UINavigationController
        • 調用UINavigationController的push方法將destinationViewController壓入棧中,完成跳轉
      • 如果segue的style是modal
        • 調用sourceViewController的presentViewController方法將destinationViewController展示出來
  • 總結上述,Segue底層實現步驟爲:

    • 1.去storyboard中查找login2Contact這個線,如果找到,就創建segue對象
    • 2.設置segue對象的來源控制器,並且創建segue的目的控制器
    • 3.通知來源控制器,準備跳轉,調用來源控制器prepareForSegue
    • 4.執行[segue perform]進行界面的跳轉
// 跳轉之前調用,通知來源控制器需要跳轉
// 作用:一般是用來數據傳值
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    ContactViewController *destVc = segue.destinationViewController;
    destVc.account = _accountField.text;
}

五 搭建添加界面

  • 聯繫人ContactViewController
- (void)viewDidLoad {
    [super viewDidLoad];

    // 設置導航條左邊
    self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"註銷" style:UIBarButtonItemStyleDone target:self action:@selector(logout)];
// 點擊註銷的時候調用
- (void)logout
{
    UIActionSheet *sheet = [[UIActionSheet alloc] initWithTitle:@"確定要註銷?" delegate:self cancelButtonTitle:@"取消" destructiveButtonTitle:@"註銷" otherButtonTitles:nil, nil];

    [sheet showInView:self.view];
}
// 點擊UIActionSheet上的按鈕的時候調用
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex
{
    NSLog(@"%ld",buttonIndex);
    // 判斷下是否點擊了註銷按鈕  
    // 回到上一個界面
    if (buttonIndex == 0) {
        [self.navigationController popViewControllerAnimated:YES];
    }
}
  • 鍵盤會跟隨 view 跳轉彈出,需要在LoginViewController登陸代碼中執行
 // 退下鍵盤
 [self.view endEditing:YES];
  • 添加聯繫人AddViewController
 - (void)viewDidLoad {
    [super viewDidLoad];    
    // 及時監聽文本框的內容改變
    [_nameField addTarget:self action:@selector(textChange) forControlEvents:UIControlEventEditingChanged];
    [_phoneField addTarget:self action:@selector(textChange) forControlEvents:UIControlEventEditingChanged];

}

 // 賬號或者密碼文本框改變的時候都會調用
 - (void)textChange
 {
    // 同時有內容,允許添加按鈕點擊
    _addBtn.enabled = _nameField.text.length && _phoneField.text.length;
}
  • 控制器完全顯示,彈出鍵盤
- (void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];
    // 主動彈出姓名文本框鍵盤
    [_nameField becomeFirstResponder];
}

六 逆傳

  • 逆傳

    • 1.首先傳遞方,要擁有接收方
    • 2.當需要傳遞的時候,直接拿到接收方就可以傳遞
  • 數據傳遞通過模型XMGContact

@interface XMGContact : NSObject
@property (nonatomic, strong) NSString *name;

@property (nonatomic, strong) NSString *phone;

+ (instancetype)contactWithName:(NSString *)name phone:(NSString *)phone;
@end
+ (instancetype)contactWithName:(NSString *)name phone:(NSString *)phone
{
    XMGContact *c = [[self alloc] init];    
    c.name = name;
    c.phone = phone;

    return c;    
}
// 點擊添加的時候
- (IBAction)add:(id)sender {  
    // 把內容包裝成聯繫人模型
    XMGContact *c = [XMGContact contactWithName:_nameField.text phone:_phoneField.text];

    // 把添加的內容傳遞給聯繫人界面
    _contactVc.contact = c;

    // 回到上一個界面(聯繫人界面)
    [self.navigationController popViewControllerAnimated:YES];
}
  • 拿到添加控制器
// 跳轉添加控制器之前調用
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
   AddViewController *addVc =  segue.destinationViewController;

    addVc.contactVc = self;
}
- (void)setContact:(XMGContact *)contact
{
    _contact = contact;
    NSLog(@"%@",contact.name);
}

六 代理解耦

  • ContactViewController太過依賴其他控制器,通過代理來解耦
  • 添加代理協議
// 定義了添加控制器的協議
@protocol AddViewControllerDelegate <NSObject>

@optional
// 當添加新的聯繫人,通知代理接收這個聯繫人
- (void)addViewControlle:(XMGAddViewController *)addVc didAddContact:(XMGContact *)contact;
@interface AddViewController : UIViewController

// 定義了一個代理屬性
@property (nonatomic, weak) id<AddViewControllerDelegate> delegate;

@end
  • 代理方法
 - (IBAction)add:(id)sender {
    // 把內容包裝成聯繫人模型
    XMGContact *c = [XMGContact contactWithName:_nameField.text phone:_phoneField.text];

    // 把添加的內容傳遞給聯繫人界面
    // 通知代理接收數據
    // _delegate = contactVc
    if ([_delegate respondsToSelector:@selector(addViewControlle:didAddContact:)]) {
        [_delegate addViewControlle:self didAddContact:c];
    }

    // 回到上一個界面(聯繫人界面)
    [self.navigationController popViewControllerAnimated:YES];
}
  • ContactViewController遵守AddViewControllerDelegate
#pragma mark - AddViewControllerDelegate方法
// 當添加界面點擊添加按鈕的時候調用,會把新增加的聯繫人模型傳遞
- (void)addViewControlle:(AddViewController *)addVc didAddContact:(XMGContact *)contact
{
    NSLog(@"%@",contact.name);
}

拿到添加控制器,設置添加控制器的代理爲聯繫人控制器,聯繫人控制器監聽添加控制器點擊,接受添加控制器的數據

  • 逆傳:一般是通過代理
    • 1.首先在傳遞方聲明代理協議,並且定義代理屬性
    • 2.在接收方跳轉到傳遞方之前的時候,讓接收方成爲傳遞方的代理
    • 3.在恰當的時候,通知代理接收數據

七 聯繫人展示控制器

  • ContactViewController
@property (nonatomic, strong) NSMutableArray *contacts;
- (NSMutableArray *)contacts
{
    if (_contacts == nil) {
        _contacts = [NSMutableArray array];
    }
    return _contacts;
}
#pragma mark - AddViewControllerDelegate方法
// 當添加界面點擊添加按鈕的時候調用,會把新增加的聯繫人模型傳遞
// 只要添加了新的聯繫人就會調用
// 每次都保存到數組,並且刷新tableView展示最新的數據
- (void)addViewControlle:(AddViewController *)addVc didAddContact:(XMGContact *)contact
{
    [self.contacts addObject:contact];

    // 注意:一定要刷新下數據源方法
    [self.tableView reloadData];
}
  • tableView數據源方法
// 返回tableView有多少行
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return self.contacts.count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *ID = @"cell";

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];

    if (cell == nil) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:ID];
    }

    // 獲取對應的模型
    XMGContact *c = self.contacts[indexPath.row];

    cell.textLabel.text = c.name;
    cell.detailTextLabel.text = c.phone;

    return cell;
}
  • cell 分割線設置,有數據纔有分割線,沒有數據就沒有分割線
 // 實現tableView中有數據纔有分割線,沒有數據就沒有分割線
 self.tableView.tableFooterView = [[UIView alloc] init];

八 編輯界面邏輯

  • ContactViewController
// 跳轉添加控制器之前調用
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    // 獲取目的控制器
   UIViewController *destVc = segue.destinationViewController;

    if ([destVc isKindOfClass:[AddViewController class]]) { // 目的控制器是添加控制器
        AddViewController *addVc = (AddViewController *)destVc;
        addVc.delegate = self;
    }else{ // 進入編輯界面

        // 把當前tableView選中的模型傳遞給編輯界面
        EditViewController *editVc = (EditViewController *)destVc;

        // 獲取當前tableView選中的模型
        // 獲取當前ableView選中的角標
        NSIndexPath *selIndex = [self.tableView indexPathForSelectedRow];

        editVc.contact = self.contacts[selIndex.row];
        editVc.delegate = self;
    }
}
  • 選中的模型接收數據
// 選中的模型
@property (nonatomic, strong) XMGContact *contact;
  • EditViewController
- (void)viewDidLoad {
    [super viewDidLoad];

    // 控制器之間傳值的時候,不能重寫set方法給子控件賦值,一般都在viewDidLoad裏面給子控件賦值

    // 在控制器的view加載完成的時候纔去給子控件賦值
    // 顯示在文本框上
    _nameField.text = _contact.name;
    _phoneField.text = _contact.phone;

    // 及時監聽文本框的內容改變
    [_nameField addTarget:self action:@selector(textChange) forControlEvents:UIControlEventEditingChanged];
    [_phoneField addTarget:self action:@selector(textChange) forControlEvents:UIControlEventEditingChanged];

    [self textChange];
}
  • 點擊編輯,修改標題爲取消,保存按鈕和鍵盤框邏輯處理
// 點擊編輯的時候調用
- (IBAction)edit:(UIBarButtonItem *)sender {
    NSLog(@"%s",__func__);

    if ([sender.title isEqualToString:@"取消"]) { // 點擊了取消
        // 修改標題爲編輯
        sender.title = @"編輯";

        // 讓文本框不允許使用
        _nameField.enabled = NO;
        _phoneField.enabled = NO;

        // 讓保存隱藏
        _saveBtn.hidden = YES;

        // 還原文本框的內容
        _nameField.text = _contact.name;
        _phoneField.text = _contact.phone;

    }else{
        // 修改標題爲取消
        sender.title = @"取消";

        // 讓文本框允許使用
        _nameField.enabled = YES;
        _phoneField.enabled = YES;

        // 彈出電話文本框的鍵盤
        [_phoneField becomeFirstResponder];

        // 讓保存顯示
        _saveBtn.hidden = NO;
    }   
}
  • 點擊保存按鈕,通知聯繫人控制器刷新數據,通過代理實現
// 點擊保存按鈕的時候調用
- (IBAction)save:(id)sender {

    // 更新模型數據
    _contact.name = _nameField.text;
    _contact.phone = _phoneField.text;

    // 通知聯繫人控制器刷新數據
    if ([_delegate respondsToSelector:@selector(editVcDidUpdateContact:)]) {
        // 通知代理刷新數據
        [_delegate editVcDidUpdateContact:self];
    }

    // 回到聯繫控制器
    [self.navigationController popViewControllerAnimated:YES];    
}
  • 代理協議
@class XMGContact,EditViewController;
@protocol EditViewControllerDelegate <NSObject>

@optional
// 通知代理更新數據
- (void)editVcDidUpdateContact:(EditViewController *)editVc;
@end

這裏寫圖片描述

九 UITabBarController

  • UITabBarController的使用步驟
    • 初始化UITabBarController
    • 設置UIWindow的rootViewController爲UITabBarController
    • 根據具體情況,通過addChildViewController方法添加對應個數的子控制器
  • UITabBarController添加控制器的方式有2種

    • 添加單個子控制器
      - (void)addChildViewController:(UIViewController *)childController;
      
  • 設置子控制器數組
    @property(nonatomic,copy) NSArray *viewControllers;

  • 如果UITabBarController有N個子控制器,那麼UITabBar內部就會有N個UITabBarButton作爲子控件

    • UITabBarButton裏面顯示什麼內容,由對應子控制器的tabBarItem屬性決定
// 標題文字
@property(nonatomic,copy) NSString *title;

// 圖標
@property(nonatomic,retain) UIImage *image;

// 選中時的圖標
@property(nonatomic,retain) UIImage *selectedImage;

// 提醒數字
@property(nonatomic,copy) NSString *badgeValue;
  • 代碼示例
 // 添加子控制器
    // 默認tabBarVc顯示第0個子控制器的view
    UIViewController *vc = [[UIViewController alloc] init];
    vc.view.backgroundColor = [UIColor redColor];

    // 設置這個控制器對應按鈕的標題
    vc.tabBarItem.title = @"消息";
    // tabBar控制器默認會選中第0個按鈕
    // 在iOS7之後默認會把UITabBar上的選中的按鈕圖片給渲染成藍色

    // 設置這個控制器對應按鈕的圖片
    vc.tabBarItem.image = [UIImage imageNamed:@"tab_recent_nor"];

    // 設置提醒數字
    vc.tabBarItem.badgeValue = @"1000";

    [tabBarVc addChildViewController:vc];

十 主流框架

這裏寫圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章