最近和另一位同事負責公司登陸和用戶中心模塊的開發工做,開發週期計劃兩週,減去和產品和接口的協調時間,再減去因爲原型圖和接口的問題,致使強迫症糾結症狀高發,情緒不穩定耗費的時間,能在兩週基本完成也算是個不小的奇蹟了。本文就總結一下如何知足產品須要的狀況下,高效開發一個登陸註冊模塊。服務器
1.利用繼承解決界面重複性功能。一般登陸註冊會有一個獨立的設計,而模塊內部會有有類似的背景,類似的導航欄樣式,類似返回和退出行爲,類似的輸入框,按鈕樣式等。async
好比上面的的註冊和登陸模塊,就有相同的返回按鈕,相同的背景,相同的導航欄樣式,甚至相同的按鈕和輸入框樣式。因此爲了加快咱們的開發,咱們徹底先定義一個父控制器,而後經過的繼承實現多態,從而實現咱們快速設計頁面和基本功能的實現。下圖是個人我的項目《丁丁印記》的登陸註冊模塊的目錄結構,其中HooEntryBaseViewController就定義了這個模塊通用的行爲和樣式:動畫
2.彈出鍵盤和退出鍵盤機制開發。ui
這點使咱們開發者容易忽略的一點,我也由於看到一些APP由於彈出鍵盤遮擋輸入,致使怒刪APP的行爲。這模塊的設計就根據產品的設計來決定採用什麼代碼實現咱們的目的了。atom
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(closeKeyboard:)]; tap.numberOfTapsRequired = 1; tap.numberOfTouchesRequired = 1; [self.view addGestureRecognizer:tap];
- (void)closeKeyboard:(id)sender { [self.view endEditing:YES]; }
- (void)textViewDidBeginEditing:(UITextField *)textView { CGRect frame = textView.frame; int offset = frame.origin.y + 120 - (self.view.frame.size.height - 216.0);//鍵盤高度216 NSTimeInterval animationDuration = 0.30f; [UIView beginAnimations:@"ResizeForKeyBoard" context:nil]; [UIView setAnimationDuration:animationDuration]; float width = self.view.frame.size.width; float height = self.view.frame.size.height; if(offset > 0) { CGRect rect = CGRectMake(0.0f, -offset,width,height); self.view.frame = rect; } [UIView commitAnimations]; }
3.接入第三方登陸,必需要判斷用戶是否安裝該第三方客戶端,不然蘋果可能審覈沒法經過。血的教訓。spa
好比個人APP《丁丁印記》接入了QQ登陸功能,程序會客戶端是否安裝了QQ,若是未安裝則隱藏QQ登陸圖標。設計
if (![QQApi isQQInstalled]) { self.QQLoginButton.hidden = YES; self.QQLoginLabel.hidden = YES; }
4.特殊情景處理。這容易是一個空白點,一般年輕的開發的者不會考慮到這一塊,而一般產品和UE也不太會記得定義清楚臨界點的行爲。3d
#pragma mark - 登陸按鈕點擊 - (IBAction)login:(UIButton *)sender { if([self.userNameTextField.text isEmpty] || [self.passwordTextField.text isEmpty]){ [SVProgressHUD showErrorWithStatus:@"用戶名或密碼不能爲空"]; }else{ __weak typeof(self) weakSelf = self; [[HooUserManager manager] LoginWithUserName:self.userNameTextField.text andPassword:self.passwordTextField.text block:^(BmobUser *user, NSError *error) { __strong __typeof(weakSelf)strongSelf = weakSelf; if (error) { [SVProgressHUD showErrorWithStatus:@"登陸失敗"]; }else if(user){ [SVProgressHUD showSuccessWithStatus:@"登陸成功"]; [strongSelf loginSuccessDismiss]; } }]; } }
NSString *emailStr; NSString *phoneStr; NSString *passwordStr = weakSelf.passwordView.inputTextField.text; emailStr = weakSelf.accountView.inputTextField.text; if (![NSString validateEmail:emailStr] || !emailStr.length) { [weakSelf showErrorTipViewWithMessage:@"郵箱格式錯誤"]; return; } } else { phoneStr = weakSelf.accountView.inputTextField.text; if (phoneStr.length < 5) { [weakSelf showErrorTipViewWithMessage:@"手機長度錯誤")]; return; } if ([weakSelf.accountView.countryCode isEqualToString:@"+86"]) { if (![phoneStr isValidateMobileNumber]) { [weakSelf showErrorTipViewWithMessage:@"手機號碼格式錯誤")]; return; } } } if (passwordStr.length < kPasswordMinLength) { [weakSelf showErrorTipViewWithMessage:ATLocalizedString(@"密碼長度超過少於6個字符")]; return; } if (passwordStr.length > kPasswordMaxLength) { [weakSelf showErrorTipViewWithMessage:@"密碼長度超過20個字符")]; return; }
5.手機找回密碼,發送驗證碼按鈕的處理。這個行爲也容易被產品忽略須要咱們開發者主動想到,而後跟產品肯定這個需求,而後肯定按鈕的觸發後的行爲,不然用戶可能屢次點擊發送驗證碼,這會形成服務器負擔,而且可能返回給用戶多條短信,形成困擾。下面這段代碼能夠實現單擊驗證碼按鈕,而後倒計時2分鐘後恢復按鈕的可點擊狀態。code
- (void)verifedCodeButtonWithTitle:(NSString *)title andNewTitle:(NSString *)newTitle { WS(weakSelf); __block int timeout = kTimeout; dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_source_t _timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0,queue); dispatch_source_set_timer(_timer,dispatch_walltime(NULL, 0),1.0*NSEC_PER_SEC, 0); dispatch_source_set_event_handler(_timer, ^{ if(timeout<=0){ dispatch_source_cancel(_timer); dispatch_async(dispatch_get_main_queue(), ^{ [weakSelf setTitle:title forState:UIControlStateNormal]; weakSelf.userInteractionEnabled = YES; }); }else{ int seconds = timeout; NSString *strTime = [NSString stringWithFormat:@"%.2d", seconds]; dispatch_async(dispatch_get_main_queue(), ^{ [UIView beginAnimations:nil context:nil]; [UIView setAnimationDuration:1]; [weakSelf setTitle:[NSString stringWithFormat:@"%@(%@)",newTitle,strTime] forState:UIControlStateNormal]; [UIView commitAnimations]; weakSelf.userInteractionEnabled = NO; }); timeout--; } }); dispatch_resume(_timer); }
5.用戶登陸信息和狀態持久化。咱們一般會有業務層處理登陸的數據的持久,而且使用單例,可是不能依賴單例記錄用狀態,由於用戶可能會退出,因此須要從沙盒去讀取用戶狀態的字段是否存在,如用戶的ID,或者AccessToken。orm
下面這段代碼,用來持久化用戶信息
- (void)saveUserInfoWithData:(NSDictionary *)dict { NSString *userID = dict[kUserId]; NSString *email = dict[kEmail]; NSString *mobile = dict[kMobile]; [HooNSUserDefaultSerialzer setObject:memberID forKey:kUserID]; [HooNSUserDefaultSerialzer setObject:email forKey:kEmail]; [HooNSUserDefaultSerialzer setObject:mobile forKey:kMobile]; }
5.對外開發用戶信息的接口。封裝咱們的模塊。對外提供咱們的接口,一般其餘頁面須要判斷用戶是否登陸,也可能須要用戶的惟一標示符來請求數據。這一塊若是咱們作的混亂,則容易致使其餘頁面獲取用戶信息的隨意性,好比給他們開發了讀取沙盒裏讀取用戶信息的字段。咱們應該在登陸模塊統一其餘頁面獲取這些用戶信息的行爲。
#import <Foundation/Foundation.h> #import "HooSingleton.h" @interface HooUserManager : NSObject @property (nonatomic, strong) NSString *userID; SingletonH(Manager) /** * Verify user if login or not * * @return if login in return YES ,otherwise return NO */ - (BOOL)isUserLogin; /** * login out */ - (void)loginOut; @end
#import "HooUserManager.h" #import "HooNSUserDefaultSerialzer.h" static NSString * const kMobile = @"Mobile"; static NSString * const kEmail = @"Email"; static NSString * const kUserID = @"UserID"; @implementation HooUserManager SingletonM(Manager) #pragma mark - getter and setter - (NSString *)userID { NSString *userID = [HooNSUserDefaultSerialzer objectForKey:kUserID]; return userID; } - (BOOL)isUserLogin { NSString *userID = [HooNSUserDefaultSerialzer objectForKey:kUserID]; if (userID.length) { return YES; } return NO; } - (void)loginOut { [HooNSUserDefaultSerialzer removeObjectForKey:kMobile]; [HooNSUserDefaultSerialzer removeObjectForKey:kEmail]; [HooNSUserDefaultSerialzer removeObjectForKey:kUseID]; } @end
6.其餘。
其實爲了更好的用戶體驗,咱們還會提供其餘功能,如明文顯示密碼選擇按鈕、從服務器讀取郵箱格式提示、錯誤字符糾正、固然還有最重要的動畫效果。
有點困了,腦子不太好使,就寫這麼多哈。