您好,登錄后才能下訂單哦!
??近期在做app開發的時候,因為要用到app登錄功能,就自己寫了個簡單的iOS+PHP實現登錄功能的demo,經過運行能夠通過登錄測試。
??在開發過程中,也是碰到了各種各樣的問題,經過不斷的調試和改變方法,終于將所有的坑都基本上填滿了,因此,將最終完整版的代碼及相關流程記錄在此,供自己及其它需要的人查閱使用。
??Mac OS真的是一個太太太封閉的系統環境了,封閉到我已經測試了N中辦法,都沒辦法成功搭建后臺服務器——不管是使用集成軟件(如MAMP或者XAMPP),還是自行下載MySQL和MyAdmin客戶端安裝。有的時候Apache無法正常啟動,有時候MySQL又故障掉了,更悲哀的是,真機測試時,客戶端上輸入內容后,無法正常與服務器通信!逼不得已,就只能放棄了,最終采用Windows的WIN7系統的電腦做后臺服務器,然后與測試用的手機、編程用的Mac電腦處于同一無線局域網下。==如果哪位同仁能告知如何在MacBook上搭建后臺服務器且能正常工作,歡迎不吝賜教,鄙人萬分感激!==
??當在裝有WIN 7系統的電腦上配置服務器時,我使用的是WAMP集成軟件,數據庫和表的編輯操作使用的是SQLyog軟件,這樣可以有效的創建、修改表的內容。==注意,在WIN7的電腦上搭建完后臺并創建好數據庫之后,還需要進行局域網的配置工作,這樣才能讓處于同一局域網下的設備(如手機)連接到這臺電腦及后臺==。這個方法我也忘了,所以需要您和其他做PHP后臺開發的同仁咨詢。==如果您已經知道怎么做了,也歡迎不吝賜教,我好記錄在本文章中,供更多人的來學習==。
??一些約定條件如下
??使用SQLyog或者phpMyAdmin創建一個名為testAppDatabase的數據庫,“基字符集”選擇“utf8”,“數據庫排序規則”選擇“utf8_general_ci”,如下圖所示(圖像截取的是使用SQLyog軟件創建數據庫的情況,使用phpMyAdmin類似):
??然后,在testAppDatabase數據庫下,新建一個名為userInformationTable的表,“引擎”選擇“InnoDB”,“字符集”選擇“utf8”,“核對”選擇“utf8_general_ci”,最后創建列名及每一列對應的數據類型以及是否可以為空等,并設置userID為主鍵、正數、自增,如下圖所示(圖像截取的是使用SQLyog軟件創建表的情況,使用phpMyAdmin類似):
??正常情況下,每一列都最好設置為“非空”,如果用戶沒有輸入,那么可以默認使用“N/A”等填充,等用戶輸入了當前列對應的內容了,再替換掉“N/A”即可。
??因為我們是做登錄模塊的驗證,沒有經過注冊,因此,數據庫中是沒有信息的。我們可以手動先填寫一些信息,供測試使用。填寫好的內容如下圖所示(使用的phpMyAdmin客戶端插入的數據)
??==注意,此時的密碼是完全的明文密碼,未進行任何加密,這主要是為了測試方便使用,正常開發時,請務必將保存到數據庫中的密碼進行加密處理。==
??至此,數據庫相關的“配置”就處理完了,下面是php代碼相關的內容。
??在php代碼中,我們主要完成的是接收客戶端傳輸過來的數據,并將數據與數據庫進行匹配驗證,一般驗證的內容有兩點:
??因此,我們的php代碼主要就是圍繞這兩個邏輯來編寫。
??對php有一些了解的人應該知道,保存在htddoc路徑(對于使用WAMP集成的環境來說,就是www文件夾下,如下圖)下的文件,是可以被瀏覽器通過輸入網址的方式讀取到的,如果將登錄數據庫使用的賬戶和密碼信息放到這個文件夾下,那么數據庫是非常不安全的。
??因此,我們通常將連接數據庫需要的php代碼單獨編寫并保存為“.php”格式的文件,然后將這個文件放置在與“www”同級的位置,如下圖所示的“connectionToDB.php”文件。
??使用php編輯器編輯“connectionToDB.php”文件,寫入的代碼如下:
connectionToDB.php
<?php
$dbc = mysqli_connect('192.168.1.101', 'root', '你設置的登錄數據庫的密碼', 'testAppDatabase') or die("連接失敗:".mysql_error());
//連接數據庫的格式通常為
//$dbc = mysqli_connect(hostname, 登錄賬號, 登錄密碼, 數據庫的名稱) or die("連接失敗:".mysql_error());
//hostname:一般是localhost,也常設置為作為后臺的電腦的IP地址,查詢的方法是“運行->cmd->ipconfig /all”,在控制臺中找到IPv4地址。
//對于局域網,這個IP地址可能會不斷的變化,因此,如果沒有做IP固化的操作,每次使用后臺服務器時,最好都加納差一下這個IP地址,然后將“connectionToDB.php”中的IP地址換為正在使用的地址
//登錄賬號:一般是根用戶root。如果不使用根用戶,就使用數據庫擁有者為你開辟的用戶名和密碼
//登錄密碼:對應登錄賬號的密碼
//數據庫名稱:要連接的數據庫的名稱。一般一個產品只有一個數據庫,該數據庫中有很多的表
?>
??==注意:php代碼的編寫,一定要使用utf-8的編碼格式,這點要切記。下面提到的php文件均采用這種編碼格式,將不再贅述。==
??“www”目錄就想到于在瀏覽器中輸入的localhost或者192.168.1.101這個IP地址,所以能看到,我們要編寫的“login.php”在下兩級目錄下,知道這點這對于我們編寫“login.php”文件中的某些代碼是有必要的。
login.php
<?php
header('Content-type:text/html;charset=utf-8'); //代碼的方式設置編碼方式
require_once('../../../connectionToDB.php');
//一個"../"代表一級目錄,
//因為我們的“connectionToDB.php”文件與“www”文件夾在同一級目錄下
//從“login.php”追溯“connectionToDB.php”需要進過三級目錄,所以需要三個"../"
$postedData = $_REQUEST; //$_REQUEST既可以獲取到通過POST方式傳輸的數據,也可以獲取到通過GET方式傳輸的數據
//獲取用戶輸入的賬號的形式:手機號、郵箱地址還是一般用戶名
$userAccountType = $postedData['Account_Type'];
//獲取用戶輸入的賬號和密碼
$userAccount = $postedData['User_Account'];
$userPassword = $postedData['User_Password'];
//根據賬戶形式獲取對應的賬號內容,用于后面的比對
//是否賬號是否存在的標簽以及是否登錄成功的標簽
$accountBeingOrNotFlag = "0"; //0代表賬號不存在,1代表賬號存在
$loginOKOrNotFlag = "0"; //0代表登錄失敗,1代表登錄成功
switch ($userAccountType) {
case "Telephone": //賬號是手機號
$q = "SELECT * FROM userinformationtable WHERE UserTelephoneNumber = $userAccount"; //查詢數據庫有沒有這個手機號
$r = @mysqli_query($dbc, $q);
$rows = @mysqli_num_rows($r); //查詢到的信息的行數,如果行數不是0,說明查詢到了信息
if($rows) {
//行數不是0,說明有這個手機號,設置標簽的值為1
$accountBeingOrNotFlag = "1"; //賬號存在
//查詢賬號和密碼是否匹配
$qA = "SELECT * FROM userinformationtable WHERE UserTelephoneNumber = '$userAccount' and UserPassword = '$userPassword'";
$rA = @mysqli_query($dbc, $qA);
$rowsA = @mysqli_num_rows($rA);
if($rowsA) {
//行數不是0,說明賬號和密碼匹配,設置標簽值為1,登錄成功
$loginOKOrNotFlag = "1";
}else {
//行數是0,說明賬號和密碼不匹配,設置標簽值為0,登錄失敗
$loginOKOrNotFlag = "0";
}
}else {
//行數是0,說明賬號不存在,設置標簽值為0
$accountBeingOrNotFlag = "0";
}
//將標簽值保存到數組中,然后將其傳遞給客戶端,客戶端根據標簽值判斷對應的操作邏輯
$returnArr = array("accountBeingOrNotFlag" => $accountBeingOrNotFlag, "loginOKOrNotFlag" => $loginOKOrNotFlag);
//下面的兩行代碼是方便測試使用,即將我們測試的一些內容保存到一個.log文件中,然后通過查看這個文件,看結果是否是我們想要的
$dccc = print_r($returnArr, true);
file_put_contents('C://Users/Administrator/Desktop/zj.log', $dccc);
//關閉數據庫連接
mysqli_close($dbc);
//將要傳遞給客戶端的結果信息通過json編碼的形式輸出
echo json_encode($returnArr);
break;
//下面的代碼注釋和上面的這個case里面的類似,不再贅述
case "EmailAddress":
$q = "SELECT * FROM userinformationtable WHERE UserEmailAddress = $userAccount";
$r = @mysqli_query($dbc, $q);
@$rows = mysql_num_rows($r);
if($rows) {
$accountBeingOrNotFlag = "1"; //賬號存在
$qA = "SELECT * FROM userinformationtable WHERE UserEmailAddress = '$userAccount' and UserPassword = '$userPassword'";
//$qA = "SELECT * FROM userinformationtable WHERE UserTelephoneNumber = 13240132824 and UserPassword = l19880226";
$rA = @mysqli_query($dbc, $qA);
$rowsA = @mysqli_num_rows($rA);
if($rowsA) {
$loginOKOrNotFlag = "1"; //登錄成功
}else {
$loginOKOrNotFlag = "0"; //登錄失敗
}
}else {
$accountBeingOrNotFlag = "0"; //賬號不存在
}
$returnArr = array("accountBeingOrNotFlag" => $accountBeingOrNotFlag, "loginOKOrNotFlag" => $loginOKOrNotFlag);
mysqli_close($dbc);
echo json_encode($returnArr); //輸出json格式
break;
case "NormalName":
$q = "SELECT * FROM userinformationtable WHERE UserNormalName = $userAccount";
$r = @mysqli_query($dbc, $q);
@$rows = mysql_num_rows($r);
if($rows) {
$accountBeingOrNotFlag = "1"; //賬號存在
$qA = "SELECT * FROM userinformationtable WHERE UserNormalName = '$userAccount' and UserPassword = '$userPassword'";
$rA = @mysqli_query($dbc, $qA);
$rowsA = @mysqli_num_rows($rA);
if($rowsA) {
$loginOKOrNotFlag = "1"; //登錄成功
}else {
$loginOKOrNotFlag = "0"; //登錄失敗
}
}else {
$accountBeingOrNotFlag = "0"; //賬號不存在
}
$returnArr = array("accountBeingOrNotFlag" => $accountBeingOrNotFlag, "loginOKOrNotFlag" => $loginOKOrNotFlag);
mysqli_close($dbc);
echo json_encode($returnArr); //輸出json格式
break;
}
?>
??好了,和登錄有關的php代碼已經編寫完成了,下面就開始編寫iOS客戶端的代碼。
??iOS客戶端的代碼,我們將采用MVC的架構來編寫。
??可能有人會問,只是一個demo,為什么不將M也合并到V中一起寫呢?這個就和我在文章開頭提到的坑有關了。
??我們先來看一個將MVC寫在一個viewController中的例子
??我們隨便新建一個基于單視圖的工程,然后在ViewController.m文件中編寫如下代碼:
ViewController.m的viewDidLoad方法中
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
NSURL *url = [NSURL URLWithString:@"http://192.168.1.101/testApp/Login/login.php"];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url];
//設置請求方式 POST
request.HTTPMethod = @"POST";
//設置請求的超時時間
request.timeoutInterval = 60;
request.HTTPBody = [[NSString stringWithFormat:@"User_Account=%@&User_Password=%@&Account_Type=%@",@"13542138562",@"testApp123456", @"Telephone"] dataUsingEncoding:NSUTF8StringEncoding];
NSURLSession *session = [NSURLSession sharedSession];
//4 創建網絡任務 NSURLSessionTask
//通過網絡會話 來創建數據任務
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
NSLog(@"網絡請求完成");
NSString *result = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"data = %@", data);
NSLog(@"result = %@", result);
_str = result;
NSLog(@"1.2_str = %@", _str);
// dispatch_async(dispatch_get_main_queue(), ^{
//
// // do something
//
//
// _str = result;
//
// NSLog(@"1.2_str = %@", _str);
//
//
// });
}];
//5 發起網絡任務
[dataTask resume];
NSLog(@"_str = %@", _str);
}
??這段代碼本來是想完成的工作是:將登陸的信息傳遞給后臺之后,后臺進行驗證,并將驗證的結果(沒有賬號、賬號密碼不匹配、賬號密碼匹配)傳回給客戶端,然后由客戶端根據返回回來的標簽值做響應的操作。但是運行這段代碼之后,通過斷點調試,會發現,dataTaskWithRequest:completionHandler:并沒有按照順序執行,而是直接跳過,然后執行了[dataTask resume];方法,接著就是NSLog函數輸出_str的值,會發現值是空的。當viewDidLoad代碼塊全部執行完畢后(即執行到最后一個右大括號}),才會執行dataTaskWithRequest:completionHandler:代碼塊中的內容。雖然此后會更新_str的值,但此時其實客戶端已經接收了第一次的_str的值了,如果不做其它的工作,我們是很難得到想要的結果了。
??后來經過多次的調試、驗證,最終才發現,使用通知可以解決這個問題。這也就是為啥我要把M單獨寫的原因:我們可以在M里面發送通知,然后在view里面注冊通知和實現通知的方法。
??我們分別創建一個繼承于NSObject的RegisterAndLoginModel文件,一個繼承于UIViewController的LoginViewController文件,以及一個繼承于UIView的LoginView文件。
RegisterAndLoginModel.h
#import <Foundation/Foundation.h>
@interface RegisterAndLoginModel : NSObject
- (void)checkTheUserAccount : (NSString*)userAccount andPassword : (NSString*)userPassword withAccountType : (NSString*)accountType;
@end
RegisterAndLoginModel.m
#import "RegisterAndLoginModel.h"
@implementation RegisterAndLoginModel
//GET方式提交數據
- (void)checkTheUserAccount : (NSString*)userAccount andPassword : (NSString*)userPassword withAccountType : (NSString*)accountType {
NSMutableDictionary *returnDictionary = [[NSMutableDictionary alloc]initWithCapacity:2];
NSLog(@"userAccount = %@, userPassword = %@, accountType = %@", userAccount, userPassword, accountType);
//1.構造URL網絡地址
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"http://192.168.1.101/testApp/Login/login.php?User_Account=%@&User_Password=%@&Account_Type=%@",userAccount,userPassword, accountType]];
//2.構造網絡請求對象 NSURLRequest
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url];
NSLog(@"request = %@", url);
//3.設置請求方式 GET
request.HTTPMethod = @"GET";
//設置請求的超時時間
request.timeoutInterval = 60;
NSURLSession *session = [NSURLSession sharedSession];
//4 創建網絡任務 NSURLSessionTask。通過網絡會話 來創建數據任務
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
NSLog(@"網絡請求完成");
NSDictionary *jsonDic = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves error:&error];
NSLog(@"接收到的數據為%@",jsonDic);
[returnDictionary setObject:jsonDic forKey:@"returnDictionaryKey"];
[[NSNotificationCenter defaultCenter] postNotificationName:@"loginStatusInformationDictionary" object:returnDictionary];
}];
//5.發起網絡任務
[dataTask resume];
}
LoginViewController.h
#import <UIKit/UIKit.h>
#import "LoginView.h"
#import "RegisterAndLoginModel.h"
@protocol LoginViewControllerDelegate <NSObject>
@optional
- (void)goToRegisterViewController;
- (void)loginSucceed;
@end
@interface LoginViewController : UIViewController<LoginViewDelegate>
{
NSString *accountTypeString;
}
@property (assign, nonatomic) id<LoginViewControllerDelegate>loginViewControllerDelegate;
@property (strong, nonatomic) RegisterAndLoginModel *registerAndLoginModel;
@property (strong, nonatomic) LoginView *loginView;
@end
LoginViewController.m
#import "LoginViewController.h"
@interface LoginViewController ()
@end
@implementation LoginViewController
int accountIsNotNULL = 0; //賬號是否為空
int loginPasswordIsOK = 0; //密碼格式是否正確
int loginBtnPressedNumbers = 0; //登錄按鈕累計點擊次數
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
//添加通知,監測后臺服務器返回的標簽值
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(getTheLoginStatusDiecitonary:) name:@"loginStatusInformationDictionary" object:nil];
_loginView = [[LoginView alloc]initTheLoginViewWithFrame:CGRectMake(0, 0, deviceScreenWidth, deviceScreenHeight)];
_loginView.loginViewDelegate = self;
[_loginView.goToRegisterButton addTarget:self action:@selector(goToRegisterButtonPressed:) forControlEvents:UIControlEventTouchUpInside];
[_loginView.findPasswordButton addTarget:self action:@selector(findPasswordButtonPressed:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:_loginView];
_registerAndLoginModel = [[RegisterAndLoginModel alloc]init];
}
- (void)loginButtonPressed : (UIButton*)sender {
NSLog(@"點擊了登錄");
NSLog(@"loginBtnPressedNumbers = %i", loginBtnPressedNumbers);
//首先判斷用戶輸入的賬號的類型
if(![self checkPhoneNumInputWithString:_loginView.loginAccountTextField.text]) {
//不是手機號
if(![self isEmailAddress:_loginView.loginAccountTextField.text]) {
//也不是郵箱地址
accountTypeString = @"NormalName";
}else {
//是郵箱地址
accountTypeString = @"EmailAddress";
}
}else {
//是手機號
accountTypeString = @"Telephone";
}
[_registerAndLoginModel checkTheUserAccount:_loginView.loginAccountTextField.text andPassword:_loginView.loginPasswordTextField.text withAccountType:accountTypeString];
loginBtnPressedNumbers += 1;
}
- (void)goToRegisterButtonPressed : (UIButton*)sender {
NSLog(@"去注冊");
[_loginViewControllerDelegate goToRegisterViewController];
}
- (void)findPasswordButtonPressed : (UIButton*)sender {
NSLog(@"找回密碼");
}
#pragma mark - 實現LoginViewDelegate中的方法
- (void)getTheInputStringInLoginViewFromTheTextField : (NSString*)inputString withTextFieldTag : (NSInteger)tag {
if(tag == 20001) {
if (inputString.length > 0) {
accountIsNotNULL = 1;
}else {
accountIsNotNULL = 0;
}
}else {
if((inputString.length >= 8) && (inputString.length <= 20)) {
loginPasswordIsOK = 1;
}else {
loginPasswordIsOK = 0;
}
}
if(accountIsNotNULL == 1 && loginPasswordIsOK == 1) {
[_loginView.loginButton addTarget:self action:@selector(loginButtonPressed:) forControlEvents:UIControlEventTouchUpInside];
_loginView.loginButton.alpha = 1.0;
[_loginView.loginButton setBackgroundColor:[UIColor redColor]];
[_loginView.loginButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
[_loginView.loginButton setUserInteractionEnabled:YES];
}else {
[_loginView.loginButton setBackgroundColor:[UIColor colorWithRed:211/255.0 green:211/255.0 blue:211/255.0 alpha:1.0]];
[_loginView.loginButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
[_loginView.loginButton setUserInteractionEnabled:NO];
}
}
#pragma mark - 使用正則表達式判斷手機號格式是否正確
-(BOOL)checkPhoneNumInputWithString : (NSString*)telephoneString {
NSString * MOBILE = @"^1(3[0-9]|5[0-35-9]|8[025-9])\\d{8}$";
NSString * CM = @"^1(34[0-8]|(3[5-9]|5[017-9]|8[278])\\d)\\d{7}$";
NSString * CU = @"^1(3[0-2]|5[256]|8[56])\\d{8}$";
NSString * CT = @"^1((33|53|8[09])[0-9]|349)\\d{7}$";
// NSString * PHS = @"^0(10|2[0-5789]|\\d{3})\\d{7,8}$";
NSPredicate *regextestmobile = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", MOBILE];
NSPredicate *regextestcm = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", CM];
NSPredicate *regextestcu = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", CU];
NSPredicate *regextestct = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", CT];
BOOL res1 = [regextestmobile evaluateWithObject:telephoneString];
BOOL res2 = [regextestcm evaluateWithObject:telephoneString];
BOOL res3 = [regextestcu evaluateWithObject:telephoneString];
BOOL res4 = [regextestct evaluateWithObject:telephoneString];
if (res1 || res2 || res3 || res4 ) {
return YES;
}else {
return NO;
}
}
#pragma mark - 正則表達式判斷郵箱格式是否正確
- (BOOL)isEmailAddress:(NSString*)inputEmailAddress
{
NSString* emailRegex = @"[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,4}";
NSPredicate* emailTest = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", emailRegex];
return [emailTest evaluateWithObject:inputEmailAddress];
}
#pragma mark 實現通知方法
- (void)getTheLoginStatusDiecitonary :(NSNotification*) notification {
NSMutableDictionary *resultDictionary = [notification object];
NSLog(@"resultDictionary = %@", resultDictionary);
NSDictionary *judgmentDictionary = [resultDictionary objectForKey:@"returnDictionaryKey"];
NSLog(@"judgmentDictionary = %@", judgmentDictionary);
if([[judgmentDictionary objectForKey:@"accountBeingOrNotFlag"] isEqualToString:@"0"]) {
//賬號不存在,提示用戶是否去注冊
NSLog(@"對不起,賬號不存在");
//此處的操作一定要回到主線程操作,否則程序會崩潰,警告框彈不出來
dispatch_async(dispatch_get_main_queue(), ^{
// do something
UIAlertController *accountNotBeingAlert = [UIAlertController alertControllerWithTitle:@"賬號不存在" message:@"對不起,您輸入的賬號不存在,是否前去注冊?" preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:nil];
UIAlertAction *goToRegisterAction = [UIAlertAction actionWithTitle:@"去注冊" style:UIAlertActionStyleDestructive handler:^(UIAlertAction * action) {
//進入注冊界面
[_loginViewControllerDelegate goToRegisterViewController];
}];
[accountNotBeingAlert addAction:cancelAction];
[accountNotBeingAlert addAction:goToRegisterAction];
[self presentViewController:accountNotBeingAlert animated:YES completion:nil];
});
}else {
//賬號存在
//判斷賬號和密碼是否匹配
if([[judgmentDictionary objectForKey:@"loginOKOrNotFlag"] isEqualToString:@"0"]) {
//賬號和密碼不匹配
NSLog(@"賬號和密碼不匹配,請重新輸入");
if(loginBtnPressedNumbers > 2) {
if([accountTypeString isEqualToString:@"Telephone"]) {
//用戶輸入的賬號是手機號,顯示獲取短信驗證碼
//短信驗證碼一段時間內只能獲取三次,如果超過三次,那么顯示圖形驗證碼
//更新界面元素的時候,也需要回到主線程,否則程序就崩潰或者界面UI更新錯位
dispatch_async(dispatch_get_main_queue(), ^{
// do something
_loginView.verificationCodeTextField.hidden = NO;
_loginView.loginButton.frame = CGRectMake(20, 345, deviceScreenWidth - 40, 50);
_loginView.goToRegisterButton.frame = CGRectMake(20, 405, deviceScreenWidth /2 - 20, 25);
_loginView.findPasswordButton.frame = CGRectMake(deviceScreenWidth / 2, 405, deviceScreenWidth /2 - 20, 25);
});
}else {
//賬號是郵箱地址或者一般用戶名,顯示圖形驗證碼
}
}
}else {
//賬號和密碼匹配,登錄成功
//登錄成功后將登錄狀態信息保存到NSUserDefaults中,供程序調用
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:_loginView.loginAccountTextField.text forKey:@"accountStr"];
[defaults setObject:_loginView.loginPasswordTextField.text forKey:@"passwordStr"];
[defaults setObject:@"isLogin" forKey:@"isLoginStr"];
[defaults synchronize];
[_loginViewControllerDelegate loginSucceed];
}
}
}
@end
LoginView.h
#import <UIKit/UIKit.h>
@protocol LoginViewDelegate <NSObject>
- (void)getTheInputStringInLoginViewFromTheTextField : (NSString*)inputString withTextFieldTag : (NSInteger)tag;
@end
@interface LoginView : UIView
@property (assign, nonatomic) id<LoginViewDelegate>loginViewDelegate;
@property (strong, nonatomic) UITextField *loginAccountTextField;
@property (strong, nonatomic) UITextField *loginPasswordTextField;
@property (strong, nonatomic) UITextField *verificationCodeTextField;
@property (strong, nonatomic) UIButton *getVerificationCodeButton;
@property (strong, nonatomic) UIButton *loginButton;
@property (strong, nonatomic) UIButton *goToRegisterButton;
@property (strong, nonatomic) UIButton *findPasswordButton;
- (id)initTheLoginViewWithFrame : (CGRect)frame;
@end
LoginView.m
#import "LoginView.h"
#import "UIImage+CircleImageView.h" //圓形頭像
@implementation LoginView
- (id)initTheLoginViewWithFrame : (CGRect)frame {
self = [super initWithFrame:frame];
if(self) {
//賬號輸入框
_loginAccountTextField = [[UITextField alloc]initWithFrame:CGRectMake(20, 120, deviceScreenWidth - 40, 55)];
[_loginAccountTextField setClearButtonMode:UITextFieldViewModeWhileEditing];
_loginAccountTextField.placeholder = @"用戶名/郵箱地址/手機號";
_loginAccountTextField.keyboardType = UIKeyboardTypeDefault;
_loginAccountTextField.borderStyle = UITextBorderStyleRoundedRect;
_loginAccountTextField.tag = 20001;
[self addSubview:_loginAccountTextField];
UIImageView *accountTextFieldLeftImageView = [[UIImageView alloc]initWithFrame:CGRectMake(_loginAccountTextField.frame.origin.x + 30, _loginAccountTextField.frame.origin.y +5 , 45, 45)];
accountTextFieldLeftImageView.image = [UIImage imageNamed:@"Account"];
_loginAccountTextField.leftView = accountTextFieldLeftImageView;
_loginAccountTextField.leftViewMode = UITextFieldViewModeAlways;
//密碼輸入框
_loginPasswordTextField = [[UITextField alloc]initWithFrame:CGRectMake(20, 190, deviceScreenWidth - 40, 55)];
[_loginPasswordTextField setClearButtonMode:UITextFieldViewModeWhileEditing];
_loginPasswordTextField.placeholder = @"輸入8~20位字符的密碼";
_loginPasswordTextField.keyboardType = UIKeyboardTypeDefault;
_loginPasswordTextField.borderStyle = UITextBorderStyleRoundedRect;
_loginPasswordTextField.secureTextEntry = YES;
_loginPasswordTextField.tag = 20002;
[self addSubview:_loginPasswordTextField];
UIImageView *passwordTextFieldLeftImageView = [[UIImageView alloc]initWithFrame:CGRectMake(_loginPasswordTextField.frame.origin.x + 10, _loginPasswordTextField.frame.origin.y + 5, 40, 40)];
passwordTextFieldLeftImageView.image = [UIImage imageNamed:@"password"];
_loginPasswordTextField.leftView = passwordTextFieldLeftImageView;
_loginPasswordTextField.leftViewMode = UITextFieldViewModeAlways;
//驗證碼輸入框
_verificationCodeTextField = [[UITextField alloc]initWithFrame:CGRectMake(20, 260, deviceScreenWidth - 40, 55)];
[_verificationCodeTextField setClearButtonMode:UITextFieldViewModeWhileEditing];
_verificationCodeTextField.placeholder = @"輸入4位短信驗證碼";
_verificationCodeTextField.keyboardType = UIKeyboardTypeDefault;
_verificationCodeTextField.borderStyle = UITextBorderStyleRoundedRect;
_verificationCodeTextField.tag = 20003;
_verificationCodeTextField.hidden = YES;
[self addSubview:_verificationCodeTextField];
UIImageView *verificationCodeFieldLeftImageView = [[UIImageView alloc]initWithFrame:CGRectMake(_verificationCodeTextField.frame.origin.x + 30, _verificationCodeTextField.frame.origin.y +5 , 45, 45)];
verificationCodeFieldLeftImageView.image = [UIImage imageNamed:@"Account"];
_verificationCodeTextField.leftView = verificationCodeFieldLeftImageView;
_verificationCodeTextField.leftViewMode = UITextFieldViewModeAlways;
//登錄按鈕
_loginButton = [UIButton buttonWithType:UIButtonTypeCustom];
_loginButton.frame = CGRectMake(20, 275, deviceScreenWidth - 40, 50);
_loginButton.contentHorizontalAlignment = UIControlContentHorizontalAlignmentCenter;
_loginButton.titleLabel.font = [UIFont systemFontOfSize:17.0];
[_loginButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
[_loginButton setTitle:NSLocalizedString(@"LoginButtonName", nil) forState:UIControlStateNormal];
[_loginButton setBackgroundColor:[UIColor colorWithRed:211/255.0 green:211/255.0 blue:211/255.0 alpha:1.0]];
[_loginButton setUserInteractionEnabled:NO];
[self addSubview:_loginButton];
//去注冊按鈕
_goToRegisterButton = [UIButton buttonWithType:UIButtonTypeSystem];
_goToRegisterButton.frame = CGRectMake(20, 335, deviceScreenWidth /2 - 20, 25);
_goToRegisterButton.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft;
_goToRegisterButton.titleLabel.font = [UIFont systemFontOfSize:14.0];
[_goToRegisterButton setTitleColor:[UIColor blueColor] forState:UIControlStateNormal];
[_goToRegisterButton setTitle:NSLocalizedString(@"GoToRegisterButtonName", nil) forState:UIControlStateNormal];
[_goToRegisterButton setBackgroundColor:[UIColor clearColor]];
[self addSubview:_goToRegisterButton];
//找回密碼按鈕
_findPasswordButton = [UIButton buttonWithType:UIButtonTypeSystem];
_findPasswordButton.frame = CGRectMake(deviceScreenWidth / 2, 335, deviceScreenWidth /2 - 20, 25);
_findPasswordButton.contentHorizontalAlignment = UIControlContentHorizontalAlignmentRight;
_findPasswordButton.titleLabel.font = [UIFont systemFontOfSize:14.0];
[_findPasswordButton setTitleColor:[UIColor blueColor] forState:UIControlStateNormal];
[_findPasswordButton setTitle:NSLocalizedString(@"GetPasswordButtonName", nil) forState:UIControlStateNormal];
[_findPasswordButton setBackgroundColor:[UIColor clearColor]];
[self addSubview:_findPasswordButton];
//添加通知,監測輸入內容
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(loginAccountTextFieldTextDidChangeNotification:) name:UITextFieldTextDidChangeNotification object:_loginAccountTextField];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(loginPasswordTextFieldTextDidChangeNotification:) name:UITextFieldTextDidChangeNotification object:_loginPasswordTextField];
}
return self;
}
#pragma mark - 實現通知的方法
- (void)loginAccountTextFieldTextDidChangeNotification:(NSNotification *)notification {
UITextField *textField = notification.object;
[self.loginViewDelegate getTheInputStringInLoginViewFromTheTextField:textField.text withTextFieldTag : textField.tag];
}
- (void)loginPasswordTextFieldTextDidChangeNotification:(NSNotification *)notification {
UITextField *textField = notification.object;
[self.loginViewDelegate getTheInputStringInLoginViewFromTheTextField:textField.text withTextFieldTag : textField.tag];
}
@end
- [UIKeyboardTaskQueue waitUntilAllTasksAreFinished] may only be called from the main thread
??這個問題說的是需要在主線程來完成這個工作,碰到這個問題的地方是這個示例中,如果用戶輸入的賬戶不存在,彈出來一個警告框提示用戶是否要去注冊的時候,如果警告框的相關代碼沒有在主線程中操作的話,就會有這個問題。
- Main Thread Checker: UI API called on a background thread: -[UIView setHidden:]
??這個提示是說部分UI的更新需要在主線程中完成,如果沒有在主線程中完成這個操作,可能會有錯位界面。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。