iOS 初步开发完问题小记

题外话

基于市场运营针对老版本H5与本地混合app(偏h5,大概只有登录注册找回密码用户那块等是本地)无法很好的推广,公司领导决定重新改版,统一用本地模式。并且要求大概20个工作日完成。所以这段时间确实很忙,主要因素有以下几点,总体就是不确定和空白:

  • 整个项目原型不确定,不断的开会,讨论。原型断断续续的出来了,功能还是不确定,问领导,等领导答复...
  • UI这边也是,迟迟等领导答复。9.2号开始,真正的到9月中旬,才开始有了初步的原型和UI.
  • 后台接口也是空白,边调边做。
  • 项目总的人力资源:iOS 2人(1新人),安卓2人(1新人),后台接口1人(外援2人,负责接口的人同时负责后台pc),UI 1人,产品原型 1人。

后期会陆续补充整个项目的框架,以及用到的一些第三方库及使用,以及一些费时间点。

正题

iOS这边整个项目进度:大概功能已开发完成,测试那边也大概的走了一遍。解决bug中。上几个图,嘿嘿😋

首页.png
个人中心.png

以下就记录下自己觉得比较重要的知识点(个人观点哈,大咖请绕行,😀)

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调用本地,调用本地友盟分享
邀请好友页面.png

核心代码(相应代码位置自己调一下)如下:

#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的设置(设置不对,会发生一系列的错误,最折磨人的是后台那边能收到图片,但是打不开,这个时候不仅跟这个参数有关,后台那边接收方式也有关系)如下图:

上传图片.png

核心代码如下:
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的,此页上有个去投资按钮)回到产品页(如下图)

产品页.png

核心代码如下:

//转到产品列表页
- (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天的班,脑壳都快炸了,回家好好休息😝,看到这里的你,愿你周末愉快!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,802评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,109评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,683评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,458评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,452评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,505评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,901评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,550评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,763评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,556评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,629评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,330评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,898评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,897评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,140评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,807评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,339评论 2 342

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,368评论 25 707
  • 工厂模式类似于现实生活中的工厂可以产生大量相似的商品,去做同样的事情,实现同样的效果;这时候需要使用工厂模式。简单...
    舟渔行舟阅读 7,715评论 2 17
  • 巍巍雪宝顶,山下是我家; 枫叶满山谷,烟云伴红霞。 牧女回眸笑,午夜不成眠, 寄去相思苦,天涯手相牵。
    夏旭冬尔杰阅读 321评论 0 3
  • “我们各自心中都有某些不愿意摒弃的东西,即使这个东西使我们痛苦得要死。我们就是这样,就像古老的凯尔特传说中的荆棘鸟...
    野子疯阅读 461评论 0 1
  • 二战时的意大利,一个不知名的小岛上,有个叫吉娜的女人,独自开一爿酒吧,她是个成熟美丽的女子,已有些年纪,依然风情万...
    桃仙君阅读 450评论 0 1