一 搭建界面
- 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的執行(跳轉)時刻,Segue可以分爲2大類型
自動型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的style是push
- 1>根據identifier去storyboard中找到對應的線,新建UIStoryboardSegue對象
總結上述,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];