最近和另外一位同事負責公司登入和使用者中心模組的開發工作,開發週期計劃兩周,減去和產品和介面的協調時間,再減去由於原型圖和介面的問題,導致強迫症糾結癥狀高發,情緒不穩定耗費的時間,能在兩周基本完成也算是個不小的奇蹟了。本文就總結一下如何滿足產品需要的情況下,高效開發一個登入註冊模組。
1.利用繼承解決介面重複性功能。通常登入註冊會有一個獨立的設計,而模組內部會有有相似的背景,相似的導覽列樣式,相似返回和退出行為,相似的輸入框,按鈕樣式等。
比如上面的的註冊和登入模組,就有相同的返回按鈕,相同的背景,相同的導覽列樣式,甚至相同的按鈕和輸入框樣式。所以為了加快我們的開發,我們完全先定義一個父控制器,然後通過的繼承實現多態,從而實現我們快速設計頁面和準系統的實現。下圖是我的個人項目《丁丁印記》的登入註冊模組的目錄結構,其中HooEntryBaseViewController就定義了這個模組通用的行為和樣式:
2.彈出鍵盤和退出鍵盤機制開發。
這點使我們開發人員容易忽略的一點,我也因為看到一些APP因為彈出鍵盤遮擋輸入,導致怒刪APP的行為。這模組的設計就根據產品的設計來決定採用什麼代碼實現我們的目的了。
•單擊空白地區退出鍵盤代碼:
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);//鍵盤高度216NSTimeInterval 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.接入第三方登入,必須要判斷使用者是否安裝該第三方用戶端,否則蘋果可能審核無法通過。血的教訓。
比如我的APP《丁丁印記》接入了QQ登入功能,程式會用戶端是否安裝了QQ,如果未安裝則隱藏QQ登入表徵圖。
if (![QQApi isQQInstalled]) {self.QQLoginButton.hidden = YES;self.QQLoginLabel.hidden = YES;}
4.特殊情景處理。這容易是一個空白點,通常年輕的開發的者不會考慮到這一塊,而通常產品和UE也不太會記得定義清楚臨界點的行為。
• 載入狀態。當使用者發起登入或者註冊請求時需要給方便使用的提示。
#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分鐘後恢複按鈕的可點擊狀態。
- (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。
下面這段代碼,用來持久化使用者資訊
-
(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 HooUserManagerSingletonM(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.其他。
其實為了更好的使用者體驗,我們還會提供其他功能,如明文顯示密碼選擇按鈕、從伺服器讀取郵箱格式提示、錯誤字元糾正、當然還有最重要的動畫效果。