題外話
基于市場運營針對老版本H5與本地混合app(偏h5,大概只有登錄注冊找回密碼用戶那塊等是本地)無法很好的推廣,公司領導決定重新改版,統一用本地模式。并且要求大概20個工作日完成。所以這段時間確實很忙,主要因素有以下幾點,總體就是不確定和空白:
- 整個項目原型不確定,不斷的開會,討論。原型斷斷續續的出來了,功能還是不確定,問領導,等領導答復...
- UI這邊也是,遲遲等領導答復。9.2號開始,真正的到9月中旬,才開始有了初步的原型和UI.
- 后臺接口也是空白,邊調邊做。
- 項目總的人力資源:iOS 2人(1新人),安卓2人(1新人),后臺接口1人(外援2人,負責接口的人同時負責后臺pc),UI 1人,產品原型 1人。
后期會陸續補充整個項目的框架,以及用到的一些第三方庫及使用,以及一些費時間點。
正題
iOS這邊整個項目進度:大概功能已開發完成,測試那邊也大概的走了一遍。解決bug中。上幾個圖,嘿嘿??
以下就記錄下自己覺得比較重要的知識點(個人觀點哈,大咖請繞行,??)
1. tableview 多個textfield, 鍵盤遮擋問題
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
//監聽鍵盤出現和消失,解決鍵盤遮擋最后textfield問題
[[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
#pragma mark 鍵盤出現
-(void)keyboardWillShow:(NSNotification *)note
{
CGRect keyBoardRect=[note.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
self.tableview.frame = CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height - 64 - keyBoardRect.size.height);
}
#pragma mark 鍵盤消失
-(void)keyboardWillHide:(NSNotification *)note
{
self.tableview.frame = CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height - 64);
}
2. oc與js簡單交互
場景:邀請頁面加載H5,如下邀請好友頁面,思路分解:
- oc給js傳值,js那邊負責顯示邀請人數和獎勵金額
- js調用本地,查看邀請記錄 標識處理
- js調用本地,調用本地友盟分享
核心代碼(相應代碼位置自己調一下)如下:
#import <WebKit/WebKit.h>
#import <JavaScriptCore/JavaScriptCore.h>
@property (strong, nonatomic) JSContext *context;
#pragma mark - 邀請好友頁面 uiwebview代理
- (void)webViewDidFinishLoad:(UIWebView *)webView
{
self.loadCount --;
//oc call js,oc給js傳值
self.context = [[JSContext alloc] init];
self.context = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
// 打印異常
self.context.exceptionHandler =
^(JSContext *context, JSValue *exceptionValue)
{
context.exception = exceptionValue;
NSLog(@"%@", exceptionValue);
};
[self.webView stringByEvaluatingJavaScriptFromString:[NSString stringWithFormat:@"inviteInfo('%@','%@');",
self.inviteNumber,self.inviteEarn]];
__weak BannerHrefVC *weakself = self;
self.context[@"handleShare"] =
^(NSString *title,NSString *text)
{
dispatch_async(dispatch_get_main_queue(), ^(void){
NSLog(@"%@ %@",title,text);
weakself.shareTitle = title;
weakself.shareText = text;
[weakself setupUMShare];
});
};
self.context[@"goFriendList"] =
^(void)
{
dispatch_async(dispatch_get_main_queue(), ^(void){
[weakself.navigationController pushViewController:[[FriendsListVC alloc]initWithTitle:@"好友列表" ] animated:NO];
});
};
}
3. 上傳頭像
場景:個人中心,從本地拍照或者選擇相冊里面照片上傳到服務器,并更新。特別注意content-type的設置(設置不對,會發生一系列的錯誤,最折磨人的是后臺那邊能收到圖片,但是打不開,這個時候不僅跟這個參數有關,后臺那邊接收方式也有關系)如下圖:
核心代碼如下:
iOS客戶端
AFURLResponseSerialization.h 的init里面
#pragma -mark 我這邊直接改的源文件,不建議這樣改,自己在用的地方相應設置下就好
self.acceptableContentTypes = [NSSet setWithObjects:@"application/json", @"text/json", @"text/javascript",@"multipart/form-data",@"text/plain", nil];
最好自己用的地方如下設置:
AFHTTPSessionManager *session = [AFHTTPSessionManager manager];
//https
session.securityPolicy = [self customSecurityPolicy];
session.securityPolicy.allowInvalidCertificates = YES;
session.responseSerializer = [AFJSONResponseSerializer serializer];
session.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:@"application/json", @"text/json", @"text/javascript",@"multipart/form-data",@"text/plain", nil];
session.requestSerializer = [AFJSONRequestSerializer serializer];
#pragma -mark
#pragma -mark tableview delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.row == 0) {
//設置頭像
[self alterHeadPortrait];
}
}
- (void)alterHeadPortrait{
//解決iOS8在調用系統相機拍照時,會有一兩秒的停頓,然后再彈出UIImagePickConroller的問題
if([[[UIDevice
currentDevice] systemVersion] floatValue]>=8.0) {
self.modalPresentationStyle=UIModalPresentationOverCurrentContext;
}
//初始化提示框
UIAlertController *alert = [UIAlertController alertControllerWithTitle:nil message:nil preferredStyle:UIAlertControllerStyleActionSheet];
//按鈕:從相冊選擇,類型:UIAlertActionStyleDefault
[alert addAction:[UIAlertAction actionWithTitle:@"從相冊選擇" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
//初始化UIImagePickerController
UIImagePickerController *PickerImage = [[UIImagePickerController alloc]init];
//獲取方式1:通過相冊(呈現全部相冊),UIImagePickerControllerSourceTypePhotoLibrary
//獲取方式2,通過相機,UIImagePickerControllerSourceTypeCamera
//獲取方法3,通過相冊(呈現全部圖片),UIImagePickerControllerSourceTypeSavedPhotosAlbum
PickerImage.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
//允許編輯,即放大裁剪
PickerImage.allowsEditing = YES;
//自代理
PickerImage.delegate = self;
//頁面跳轉
[self presentViewController:PickerImage animated:YES completion:nil];
}]];
//按鈕:拍照,類型:UIAlertActionStyleDefault
[alert addAction:[UIAlertAction actionWithTitle:@"拍照" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action){
/**
其實和從相冊選擇一樣,只是獲取方式不同,前面是通過相冊,而現在,我們要通過相機的方式
*/
UIImagePickerController *PickerImage = [[UIImagePickerController alloc]init];
//獲取方式:通過相機
PickerImage.sourceType = UIImagePickerControllerSourceTypeCamera;
PickerImage.allowsEditing = YES;
PickerImage.delegate = self;
[self presentViewController:PickerImage animated:YES completion:nil];
}]];
//按鈕:取消,類型:UIAlertActionStyleCancel
[alert addAction:[UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:nil]];
[self presentViewController:alert animated:YES completion:nil];
}
#pragma mark
#pragma mark PickerImage完成后的代理方法
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary<NSString *,id> *)info{
//定義一個newPhoto,用來存放我們選擇的圖片。
UIImage *newPhoto = [info objectForKey:@"UIImagePickerControllerEditedImage"];
self.ivHead.image = newPhoto;
//上傳頭像到服務器
[self reqUpdateHeader:newPhoto];
[self dismissViewControllerAnimated:YES completion:nil];
}
#pragma mark
#pragma mark 上傳頭像到服務器
- (void)reqUpdateHeader:(UIImage *)image{
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
manager.responseSerializer = [AFJSONResponseSerializer serializer];
manager.requestSerializer = [AFJSONRequestSerializer serializer];
NSString *strURL = [NSString stringWithFormat:@"http://test.helloan.cn/mobile/rest/uploadPhoto/savePhoto/1?token=%@",
[PersonalCenterData sharedInstance].userInfo.token];
[manager POST:strURL parameters:nil
constructingBodyWithBlock:^(id<AFMultipartFormData> _Nonnull formData) {
NSData *fileData = UIImageJPEGRepresentation(image, 0.1);
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
formatter.dateFormat = @"yyyyMMddHHmmss";
NSString *str = [formatter stringFromDate:[NSDate date]];
NSString *fileName = [NSString stringWithFormat:@"%@.jpg", str];
[formData appendPartWithFileData:fileData name:@"fileName" fileName:fileName mimeType:@"image/jpg"];
} progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
NSLog(@"%@",responseObject);
if ([[responseObject objectForKey:@"code"] isEqualToString:Response_OK]) {
[PersonalCenterData sharedInstance].userInfo.logoUrl = [responseObject objectForKey:@"logoUrl"];
}
[self.view makeToast:[responseObject objectForKey:@"message"]];
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
NSLog(@"%@",error);
}];
}
java后臺
DiskFileItemFactory factory = new DiskFileItemFactory();
//設置工廠的緩沖區的大小,當上傳的文件大小超過緩沖區的大小時,就會生成一個臨時文件存放到指定的臨時目錄當中。
factory.setSizeThreshold(1024*100);//設置緩沖區的大小為100KB,如果不指定,那么緩沖區的大小默認是10KB
//創建一個文件上傳解析器
ServletFileUpload upload = new ServletFileUpload(factory);
//解決上傳文件名的中文亂碼
upload.setHeaderEncoding("UTF-8");
//設置上傳單個文件的大小的最大值,目前是設置為1024*1024字節,也就是1MB
upload.setFileSizeMax(1024*1024);
//設置上傳文件總量的最大值,最大值=同時上傳的多個文件的大小的最大值的和,目前設置為10MB
// upload.setSizeMax(1024*1024*10);
//4、使用ServletFileUpload解析器解析上傳數據,解析結果返回的是一個List<FileItem>集合,每一個FileItem對應一個Form表單的輸入項
List<FileItem> list = upload.parseRequest(request);
4. label自動換行問題
網上查挺多的,這里只是記錄下,以后好查閱??
UILabel *lb = [[UILabel alloc]init];
lb.numberOfLines = 0;
lb.lineBreakMode = NSLineBreakByWordWrapping;
5. label設置部分字體顏色
其實可以跟上面寫在一起的,但是懶得改下面的序列號了,真的好懶啊,莫怪??
UILabel *lb = [[UILabel alloc]initWithFrame:CGRectMake(ContentX, 0, kScreenWidth-ContentX, kRowHeight+10)];
lb.textColor = [UIColor grayColor];
NSString *str1 = @"密碼為6-15個字符,建議字母數字和特殊字符混合,登錄密碼必須和交易密碼不同。新用戶交易密碼為登錄密碼。";
NSMutableAttributedString *str = [[NSMutableAttributedString alloc]
initWithString:str1];
// //設置字號
// [str addAttribute:NSFontAttributeName value:font range:range];
//設置文字顏色
NSRange range =[str1 rangeOfString:@"新用戶交易密碼為登錄密碼。"];
[str addAttribute:NSForegroundColorAttributeName value:kColorHeadPerson range:range];
lb.attributedText = str;
lb.numberOfLines = 0;
lb.lineBreakMode = NSLineBreakByWordWrapping;
lb.font = kFontText;
[head addSubview:lb];
6. 動態獲取cell高度
待寫
7. 重寫導航左側返回按鈕
如下代碼也對導航和底部tab的顯示和隱藏做了處理
#pragma -mark viewLife
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
self.automaticallyAdjustsScrollViewInsets = NO;
self.navigationController.navigationBar.barTintColor = kColorHeadPerson;
//半透明狀態
self.navigationController.navigationBar.translucent = NO;
self.navigationController.navigationBar.tintColor = [UIColor whiteColor];
//設置文字前景色
[self.navigationController.navigationBar setTitleTextAttributes:
@{NSFontAttributeName:[UIFont systemFontOfSize:22],
NSForegroundColorAttributeName:[UIColor whiteColor]}];
//重寫返回按鈕
self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc]initWithImage:[UIImage imageNamed:@"back_normal"]
style:UIBarButtonItemStylePlain
target:self
action:@selector(buttonBack)];
self.hud = [[MBProgressHUD alloc]initWithView:self.view];
self.hud.delegate = self;
self.hud.mode = MBProgressHUDModeIndeterminate;
self.hud.label.text=@"加載中,請稍后!";
self.hud.backgroundColor =[UIColor lightGrayColor];
[self.view addSubview:self.hud];
}
- (void)buttonBack
{
[self.navigationController popViewControllerAnimated:NO];
}
-(void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
[self.navigationController setNavigationBarHidden:NO animated:YES];
self.tabBarController.tabBar.hidden = YES;
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
self.tabBarController.tabBar.hidden = NO;
}
8. 程序延遲執行
場景:操作完之后,例如修改密碼完成之后,先toast,之后延時3秒再進行頁面跳轉。若不延時,toast顯示是看不到的。
網上后來看到有三種方式,大概為:performSelector,nstimer,gcd. 我這邊用的是gcd
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
//your code
});
9. 從普通vc到tabitem中的一個vc
場景:從實名認證成功頁面(該頁面為從個人中心也所push的,此頁上有個去投資按鈕)回到產品頁(如下圖)
核心代碼如下:
//轉到產品列表頁
- (void)goInvest
{
self.tabBarController.selectedIndex = 1;
[self.navigationController popToRootViewControllerAnimated:NO];
}
10. 列表上拉彈回問題
- (void)viewDidLoad {
[super viewDidLoad];
self.tableview = [[UITableView alloc]initWithFrame:self.view.bounds style:UITableViewStyleGrouped];
self.tableview.dataSource = self;
self.tableview.delegate = self;
self.tableview.scrollEnabled = YES;
[self.view addSubview:self.tableview];
}
-(void)scrollViewDidScroll:(UIScrollView *)scrollView
{
self.tableview.contentSize =CGSizeMake(0, kRowHeight*5+ButtonHeight +40+10);
}
11. 充值(調用第三方寶付接口)
充值接口目前這個版本走的其實都是web,api貌似要貴好多,以后版本公司可能會改用api。多一句,web真的好慢,其實充值這么慢,無形中會流失掉一部分想投資的用戶。
#pragma -mark
#pragma -mark 充值獲取交易號
- (void)doCharge
{
NSLog(@"doCharge");
NSString *token = [PersonalCenterData sharedInstance].userInfo.token;
if (!token) {
[self.view makeToast:NoLoginMsg];
return;
}
[[PersonalCenterLogicManager sharedInstance]postChargeWithToken:token amount:[self.tfAmount.text intValue] success:^(NSDictionary *dic) {
if ([[dic objectForKey:@"code"] isEqualToString:Response_OK]) {
NSString *tradeNo =[dic objectForKey:@"tradeNo"];
if (tradeNo) {
//跳轉到第三方寶付充值
BaoFooPayController*web = [[BaoFooPayController alloc] init];
web.PAY_TOKEN = tradeNo;
web.delegate = self;
[self presentViewController: web animated:YES completion:^{ }];
}
}
else{
[self.view makeToast:[dic objectForKey:@"message"]];
}
} failure:^(NSError *err) {
[self.view makeToast:ServerError];
}];
}
#pragma mark - BaofooDelegate
-(void)callBack:(NSString*)params
{
NSLog(@"返回的參數是:%@",params);
if ([params rangeOfString:@"支付成功"].location != NSNotFound) {
//條件為真,表示字符串searchStr包含一個自字符串substr
ChargeSuccessVC *vc = [[ChargeSuccessVC alloc]initWithTitle:@"充值成功"];
vc.chargeAmount = self.tfAmount.text;
vc.bankNumber = self.bankInfo.bankNumber;
[self.navigationController pushViewController:vc animated:NO];
}else
{
//條件為假,表示不包含要檢查的字符串
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"提示" message:[NSString stringWithFormat:@"支付結果:%@",params] delegate:nil cancelButtonTitle:@"確定" otherButtonTitles:nil, nil];
[alert show];
}
}
還需待寫的有
1 銀行選擇
2 二級聯動城市選擇
3 頂部滾動等
連續上7天的班,腦殼都快炸了,回家好好休息??,看到這里的你,愿你周末愉快!