簡介
雖然目前市面上有一些不錯的加密相冊App,但不是內(nèi)置廣告,就是對上傳的張數(shù)有所限制。本文介紹了一個加密相冊的制作過程,該加密相冊將包括多密碼(輸入不同的密碼即可訪問不同的空間,可掩人耳目)、WiFi傳圖、照片文件加密等功能。目前項目和文章會同時前進,項目的源代碼可以在github上下載。
點擊前往GitHub
概述
上一篇文章主要介紹了賬戶存儲類與工具類的設計,這一篇將通過工具類,實現(xiàn)登陸與注冊的交互界面。
登錄控制器與視圖設計
自定義控制器視圖
為了分離視圖邏輯與業(yè)務邏輯,控制器視圖用一個自定義類去管理,在控制器的loadView方法中將自定義視圖指定為控制器視圖。
文件結(jié)構(gòu)如下。

指定自定義視圖為控制器視圖的方法如下。
由于要使用自定義視圖SGWelcomeView的一些API,因此需要將其引用,否則直接通過view獲取的還需要類型強轉(zhuǎn)才能使用。
@interface SGWelcomeViewController ()
@property (nonatomic, weak) SGWelcomeView *welcomeView;
@end
@implementation SGWelcomeViewController
- (void)loadView {
SGWelcomeView *view = [SGWelcomeView new];
self.view = view;
self.welcomeView = view;
}
登錄界面設計
登錄界面如下圖所示。

嘗試Touch ID的登錄方式
如果設備支持Touch ID,則會先嘗試Touch ID的驗證方式,如果驗證失敗則要求輸入密碼。
在登錄控制器出現(xiàn)時,先清空已經(jīng)登錄的賬戶,并且嘗試Touch ID登錄的實現(xiàn)如下。
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[SGAccountManager sharedManager].currentAccount = nil;
[self handleTouchIDLogin];
}
- (void)handleTouchIDLogin {
LAContext *context = [LAContext new];
if([context canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:nil]) {
[context evaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics localizedReason:@"Agony need your Touch ID to login" reply:^(BOOL success, NSError * _Nullable error) {
if (success) {
SGAccount *account = [[SGAccountManager sharedManager] getTouchIDAccount];
[self loginWithAccount:account];
} else {
[self handleCommonLogin];
}
}];
} else {
[self handleCommonLogin];
}
}
Touch ID的驗證通過LAContext實現(xiàn),成功則通過Touch ID綁定的密碼登錄,失敗則進行密碼登錄(調(diào)用handleCommonLogin方法)。
需要注意Touch ID的回調(diào)是在子線程,如果涉及到UI操作,不要忘記放到主線程操作。
密碼登錄方式
密碼登錄通過調(diào)用handleCommonLogin方法實現(xiàn),該方法指定登錄view的block回調(diào),并且使得密碼輸入框成為第一響應者。
- (void)handleCommonLogin {
WS(); // 定義weakSelf的宏
[self.welcomeView setWelcomeHandler:^(SGAccount *account) {
[weakSelf loginWithAccount:account];
}];
}
當用戶輸入完密碼按下Return鍵,會回調(diào)該block來執(zhí)行登錄。注意到Touch ID和密碼登錄最后執(zhí)行的方法都是loginWithAccount:方法,該方法的實現(xiàn)如下。
- (void)loginWithAccount:(SGAccount *)account {
dispatch_async(dispatch_get_main_queue(), ^{
if (!account) {
[MBProgressHUD showError:@"Password Error"];
return;
}
[SGAccountManager sharedManager].currentAccount = account;
AppDelegate *app = [UIApplication sharedApplication].delegate;
app.window.rootViewController = [[UINavigationController alloc] initWithRootViewController:[SGHomeViewController new]];
});
}
之所以使用GCD,是因為在Touch ID的回調(diào)里也進行了該方法的調(diào)用,而Touch ID的回調(diào)是子線程。
在登錄驗證時如果密碼驗證成功返回賬戶對象,失敗則返回空,因此通過賬戶是否為空可以判斷是否登錄完成。如果登錄成功則保存登陸成功的對象到賬戶管理單例中,并且切換根控制器為相冊主頁的控制器。
登錄視圖的細節(jié)
對于屏幕尺寸較小的手機,鍵盤可能會遮擋輸入框,應該監(jiān)聽鍵盤的顯示與隱藏事件,判斷鍵盤是否遮擋了輸入框,從而決定是否要向上移動視圖。
登錄界面包含了圖標、文本和輸入框,三者通過約束來定位在父視圖SGWelcomeView上,在位移時,為了方便起見,將整個父視圖向上平移,平移通過transform來實現(xiàn),具體如下。
注冊與注銷通知
由于視圖通過代碼框架,因此一定會調(diào)用initWithFrame:方法,在該方法中注冊通知。
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardShow:) name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardHide:) name:UIKeyboardWillHideNotification object:nil];
在dealloc方法中注銷通知。
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
處理鍵盤的顯示
由于監(jiān)聽的是鍵盤即將動作的事件,因此鍵盤的最終位置應通過endFrame來拿到。判斷鍵盤是否遮擋輸入框的關(guān)鍵是看鍵盤的topY是否小于輸入框的bottomY,如果topY小于bottomY,則應該將整個視圖向上平移bottomY-topY,為了美觀,應該多平移一段間距,deltaY就是應該平移的距離,向上為負值。
條件判斷中除去delta還判斷了視圖是否還實施了變換,這是因為變換是針對整個父視圖的,而計算時獲取的輸入框坐標是相對父視圖的,因此父視圖的變換不會影響到輸入框坐標,鍵盤顯示可能被多次調(diào)用,為了保證不會重復的將視圖向上平移,需要判斷是否已經(jīng)進行過變換了。
- (void)keyboardShow:(NSNotification *)nof {
CGRect endFrame = [nof.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
CGFloat endY = endFrame.origin.y;
CGFloat deltaY = endY - CGRectGetMaxY(self.pwdFiled.frame) - 10;
if (deltaY < 0 && CGAffineTransformEqualToTransform(self.transform, CGAffineTransformIdentity)) {
self.transform = CGAffineTransformTranslate(self.transform, 0, deltaY);
}
}
處理鍵盤的隱藏
鍵盤隱藏時只需要復原變換即可。
- (void)keyboardHide:(NSNotification *)nof {
self.transform = CGAffineTransformIdentity;
}
如果想要讓鍵盤的出現(xiàn)、消失與視圖位移同步移動,可以通過通知對象拿到鍵盤移動的duration,然后將變換寫在UIView的動畫block中。
注冊控制器與視圖設計
注冊控制器的設計與登錄控制器設計一致,文件結(jié)構(gòu)如下。

界面如下。

注冊頁面與登錄頁面一樣,也是通過block回調(diào)到控制器,來處理注冊,因為每個密碼對應一個存儲空間,注冊時要求密碼不能與已有賬戶重復。當輸入完確認密碼并按下鍵盤上的Return鍵后通過block回調(diào),傳回密碼與確認密碼,控制器處理的實現(xiàn)如下。
- (void)handleRegisterWithPassword:(NSString *)password confirm:(NSString *)confirm {
// 密碼與確認密碼必須一致,并且不為空
if (![password isEqualToString:confirm]) {
[MBProgressHUD showError:@"Passwords Do Not Match"];
return;
} else if (!password.length) {
[MBProgressHUD showError:@"Password Cannot be Empty"];
return;
}
// 使用賬戶管理對象來處理注冊的業(yè)務邏輯
SGAccountManager *mgr = [SGAccountManager sharedManager];
NSString *errorMessage = nil;
[mgr registerAccountWithPassword:password errorMessage:&errorMessage];
if (errorMessage == nil) {
// 注冊成功則回到登錄頁面
[MBProgressHUD showSuccess:@"Register Succeeded"];
[self.navigationController popViewControllerAnimated:YES];
} else {
[MBProgressHUD showError:errorMessage];
}
}
總結(jié)
本文主要介紹了登錄與注冊界面的實現(xiàn)細節(jié),歡迎關(guān)注項目后續(xù),項目的下載地址在本文的開頭可以找到。