IOS开发学习周报(四)

IOS开发学习周报(四)

简介

课程名称IOS开发实训任课老师郑贵锋老师&字节跳动工程师
学号16340015专业(方向)软件工程(计应)
姓名陈彬彬Email944131226@qq.com
开始日期2019/04/06完成日期2019/04/11


本周概括

学习记录:

  • 学习ios-oc项目纯代码开发
  • 学习UI控件
    • UIAlertView
    • UIAlertController
    • UITextField
  • 学习网络访问POST请求

工作记录:

  • 继续完成一个简答的网络访问项目 worldcup-demo
    • 重构项目,实现无main.storyboard的纯代码开发
    • 增加登陆界面,简单的POST请求后台实现登陆校验功能
    • 丰富查询API世界杯射手榜功能:
      • 实现 UITableView 界面点击cell加载更多球员信息

学习记录

ios-oc项目纯代码开发

参考

参考博客: https://www.cnblogs.com/chars/p/5150155.html

简要步骤

  1. 新建一个 Single View Application 模板OC项目

  2. 删除项目文件 Main.storyboardLaunchScreen.xlb (可不做)

  3. 在project配置中,General -> Deployment Info -> Main Interface 置为空

    pic

  4. AppDelegate.m中添加代码

    记得导入 ViewController.h

    #import "ViewController.h"
    
    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
        // Override point for customization after application launch.
        self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
        ViewController *viewController = [[ViewController alloc] init];
        self.window.rootViewController = viewController;
        self.window.backgroundColor = [UIColor whiteColor];
      [self.window makeKeyAndVisible];
        
       return YES;
    }
    
  5. 这样,你APP的启动页就是 ViewController 控制的界面了


[UI控件] UIAlertView

参考:

参考博客:UIAlertView 和 UIAlertController 的用法

参考博客:如何使用 UIAlertController 实现各种样式的弹窗

实例:

使用 UIAlertView 实现一个简单弹窗:

  1. .m 文件中声明实现 UIAlertViewDelegate 协议
@interface NetworkAccessViewController() <UIAlertViewDelegate>

@end
  1. 创建、配置、展示 UIAlertView 弹窗
// 使用UIAlertView 弹出提示框
// UIAlertView 在 ios 9.0及以后不被推荐使用
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"提示框" message:@"网络访问出现了一点问题" delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil, nil];
[alert show];

效果:(忽略背景色)

pic


[UI控件] UIAlertController

参考:

参考博客:UIAlertView 和 UIAlertController 的用法

参考博客:如何使用 UIAlertController 实现各种样式的弹窗

实例:

使用 UIAlertController 实现一个简单弹窗

// 使用UIAlertController来替换UIAlertView
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"提示框" message:@"网络访问出现了一点问题" preferredStyle:UIAlertControllerStyleAlert];
// 添加按钮的响应事件
UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"好的" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
    // Do something
}];
[alert addAction:okAction];
// 弹出提示框
[self presentViewController:alert animated:YES completion:nil];

效果:

pic


[UI控件] UITextField

参考:

参考博客:IOS—UITextFiled控件详解

常用方法和属性:

// 初始化textfield并设置位置及大小
UITextField *text = [[UITextField alloc]initWithFrame:CGRectMake(20, 20, 130, 30)];
//设置边框样式,只有设置了才会显示边框样式  
text.borderStyle = UITextBorderStyleRoundedRect;
//设置输入框的背景颜色
text.backgroundColor = [UIColor whiteColor];
//设置背景
text.background = [UIImage imageNamed:@"dd.png"];
//首字母是否大写
text.autocapitalizationType = UITextAutocapitalizationTypeNone;
//是否纠错
text.autocorrectionType = UITextAutocorrectionTypeNo;
//密语输入
text.secureTextEntry = YES;
//最右侧加图片是以下代码   左侧类似
UIImageView *image=[[UIImageView alloc] initWithImage:[UIImage imageNamed:@"right.png"]];
text.rightView=image;
text.rightViewMode = UITextFieldViewModeAlways; 
// 设置提示信息
[usernameTextField setPlaceholder:@"Username"];

实例:

// 用户名输入框
self.usernameTextField = ({
    UITextField *usernameTextField = [[UITextField alloc] initWithFrame:CGRectMake(50, 200, self.view.frame.size.width - 100, 50)];
    [usernameTextField setBorderStyle:UITextBorderStyleRoundedRect];
    [usernameTextField setClearButtonMode:UITextFieldViewModeAlways];
    [usernameTextField setAutocorrectionType:UITextAutocorrectionTypeNo];
    [usernameTextField setAutocapitalizationType:UITextAutocapitalizationTypeNone];
    [usernameTextField setPlaceholder:@"Username"];
    usernameTextField;
});
// 添加控件
[self.view addSubview:self.usernameTextField];

效果:

picture

pic


网络访问 POST 请求

流程

以发起一个 POST 请求为例子:

  • 创建Session。
  • 构建可变的Request对象
  • 配置Request为POST请求,添加Header头部和Body
  • 创建NSURLSessionDataTask。
  • 执行Task。
  • Response处理。

允许IOS9 HTTP请求限制

PIC

用法实例:

以一次 HTTP POST请求为例:

// 构建并配置 Session
NSURLSessionConfiguration *defaultConfigObject = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *delegateFreeSession = [NSURLSession sessionWithConfiguration:defaultConfigObject delegate:nil delegateQueue:[NSOperationQueue mainQueue]];
// 构建并配置 UrlRequest 网络请求
NSURL *url = [NSURL URLWithString:@"http://chenbb6.cn:3010/login"];
NSMutableURLRequest *urlRequest = [NSMutableURLRequest requestWithURL:url];
// 设置请求方法为 POST
[urlRequest setHTTPMethod:@"POST"];
// 设置请求 Header 头部
NSDictionary *headDict = [[NSDictionary alloc] initWithObjects:@[@"application/json"] forKeys:@[@"Content-Type"]];
[urlRequest setAllHTTPHeaderFields:headDict];
// 设置请求 Body 主体
NSString *username = self.usernameTextField.text;
NSString *password = self.passwordTextField.text;
NSDictionary *bodyDict = [[NSDictionary alloc] initWithObjects:@[username, password] forKeys:@[@"username", @"password"]];
NSData *bodyData = [NSJSONSerialization dataWithJSONObject:bodyDict options:NSJSONWritingPrettyPrinted error:nil];
[urlRequest setHTTPBody:bodyData];
// 构建 NSURLSessionDataTask
NSURLSessionDataTask *dataTask = [delegateFreeSession dataTaskWithRequest:urlRequest completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {

    if(nil == error) {
        // Response 处理
    }
    else {
        // Error 处理
    }
}];
// 执行 Task
[dataTask resume];

工作记录

项目要求

基于自己搭建的简单后台,继续完成一个简答的网络访问项目 worldcup-demo

  • 重构代码,实现无main.storyboard的纯代码开发
  • 增加登陆界面,简单的POST请求后台实现登陆校验功能
  • 丰富查询API世界杯射手榜功能:
    • 实现 UITableView 界面点击cell加载射手榜更多球员信息

项目结构

picture


登陆界面

实现步骤:

  1. UI布局:
    • 添加两个 UITextField 和 一个 UIButton
    • 设置控件属性,设置背景色
    • 去除导航栏标题栏
  2. 后台响应:
    • 添加 UIButton 的响应函数,进行POST网络请求
    • POST 请求 Error处理
      • 弹窗警示网络故障
    • POST请求 Response 处理
      • 若服务器返回登陆成功,跳转到主页界面(账号:root,密码:admin,为测试账号)
      • 登陆失败,弹窗提示账号密码不匹配

参考博客:

参考博客:UIAlertView 和 UIAlertController 的用法

参考博客:如何使用 UIAlertController 实现各种样式的弹窗

参考博客:IOS—UITextFiled控件详解

ios开发之–NSDictionary和NSData之间的互转/NSString和NSData之间的互转

关键代码:

密码输入框实现:

// 密码输入框
self.passwordTextField = ({
    UITextField *passwordTextField = [[UITextField alloc] initWithFrame:CGRectMake(50, 300, self.view.frame.size.width - 100, 50)];
    [passwordTextField setBorderStyle:UITextBorderStyleRoundedRect];
    [passwordTextField setClearButtonMode:UITextFieldViewModeAlways];
    [passwordTextField setAutocorrectionType:UITextAutocorrectionTypeNo];
    [passwordTextField setAutocapitalizationType:UITextAutocapitalizationTypeNone];
    [passwordTextField setPlaceholder:@"Password"];
    [passwordTextField setSecureTextEntry:YES];
    passwordTextField;
});
[self.view addSubview:self.passwordTextField];

POST请求设置 Header 头部 和 Body 主体:

// 构建并配置 UrlRequest 网络请求
NSURL *url = [NSURL URLWithString:@"http://chenbb6.cn:3010/login"];
NSMutableURLRequest *urlRequest = [NSMutableURLRequest requestWithURL:url];
// 设置请求方法为 POST
[urlRequest setHTTPMethod:@"POST"];
// 设置请求 Header 头部
NSDictionary *headDict = [[NSDictionary alloc] initWithObjects:@[@"application/json"] forKeys:@[@"Content-Type"]];
[urlRequest setAllHTTPHeaderFields:headDict];
// 设置请求 Body 主体
NSString *username = self.usernameTextField.text;
NSString *password = self.passwordTextField.text;
NSDictionary *bodyDict = [[NSDictionary alloc] initWithObjects:@[username, password] forKeys:@[@"username", @"password"]];
NSData *bodyData = [NSJSONSerialization dataWithJSONObject:bodyDict options:NSJSONWritingPrettyPrinted error:nil];
[urlRequest setHTTPBody:bodyData];

POST请求中进行Response 和 Error处理:

// 构建 NSURLSessionDataTask
NSURLSessionDataTask *dataTask = [delegateFreeSession dataTaskWithRequest:urlRequest completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
    // Response 处理
    if(nil == error) {
        // NSData 转 NSDictionary
        NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves error:nil];
        NSNumber *status_code = [dict valueForKey:@"status_code"];
        if([status_code intValue] == 200) {
            NSLog(@"登录成功");
            NetworkAccessViewController *controller = [[NetworkAccessViewController alloc] init];
            controller.hidesBottomBarWhenPushed = YES;
            [self.navigationController pushViewController:controller animated:YES];
        }else {
            NSLog(@"登陆失败");
            // 使用UIAlertController来替换UIAlertView
            UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"提示框" message:@"账号密码错误" preferredStyle:UIAlertControllerStyleAlert];
            // 添加按钮的响应事件
            UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"重试一次" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
                // Do something
            }];
            [alert addAction:okAction];
            // 弹出提示框
            [self presentViewController:alert animated:YES completion:nil];
        }

    }
    else {
        // 使用UIAlertController来替换UIAlertView
        UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"提示框" message:@"网络访问出现了一点问题" preferredStyle:UIAlertControllerStyleAlert];
        // 添加按钮的响应事件
        UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"好的" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
            // Do somethin
        }];
        [alert addAction:okAction];
        // 弹出提示框
        [self presentViewController:alert animated:YES completion:nil];
    }
}];

实现效果:

网络错误或服务器错误:

PIC

账号密码错误:

pic

正确账号登陆:(username=root, password = admin)

picture

picture


丰富射手榜界面

实现步骤:

  1. 实现一个 LoadingTableViewCell 类——继承于 UITableViewCell
    • 三种Cell状态:加载更多、正在加载中、加载完毕
    • 两个子控件:UILabel + UIActivityIndicatorView
    • 设置三种Cell状态下,UILabel 的文字、位置和UIActivityIndicatorView 的位置、动画起停
  2. 修改射手榜界面
    • 修改 UITableViewDataSource 的协议实现,一次展示射手榜十位运动员信息+一个加载Cell
    • 设置点击加载Cell的响应函数,每点击一次,添加其后10位运动员的信息到 UITableView ,加载Cell状态动态变换,直到加载完毕。

参考博客:

参考博客:UITableView与自定义UITableViewCell

iOS layoutSubview的方法总结/重绘drawRect

关键代码:

自定义的加载Cell:

@implementation LoadingTableViewCell

    - (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
    if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) {
        self.tipsLabel = [[UILabel alloc] initWithFrame:CGRectZero];
        [self.contentView addSubview:self.tipsLabel];

        self.indicatorView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
        [self.contentView addSubview:self.indicatorView];

        [self updateData];
    }
    return self;
}

- (void)layoutSubviews {
    [self.tipsLabel sizeToFit];
    // 居中显示 Cell
    if (LoadingStatusLoding == self.status) {
        CGFloat indicatorWidth = self.indicatorView.frame.size.width;
        CGFloat labelWidth = self.tipsLabel.frame.size.width;
        CGFloat space = 5;
        CGFloat leftMargin = (self.bounds.size.width-indicatorWidth-space-labelWidth)/2;
        [self.tipsLabel setCenter:CGPointMake(leftMargin + labelWidth/2, self.bounds.size.height/2)];
        [self.indicatorView setCenter:CGPointMake(leftMargin + labelWidth + space + indicatorWidth/2, self.bounds.size.height/2)];
    } else{
        [self.tipsLabel setCenter:CGPointMake(self.bounds.size.width/2, self.bounds.size.height/2)];
    }
}

-(void)setStatus:(LoadingStatus)status {
    if(_status == status) {
        return;
    }
    _status = status;
    [self updateData];

    [self setNeedsLayout];
}

- (void)updateData {
    if (LoadingStatusDefault == _status) {
        [self.tipsLabel setText:@"点击加载更多"];
        [self.indicatorView stopAnimating];
    } else if (LoadingStatusLoding == _status) {
        [self.tipsLabel setText:@"正在加载..."];
        [self.indicatorView startAnimating];
    } else if (LoadingStatusNoMore == _status) {
        [self.tipsLabel setText:@"没有更多数据了"];
        [self.indicatorView stopAnimating];
    }
}

@end

UITableViewDataSource 协议实现(关键代码)

- (UITableViewCell *)tableView: (UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    BOOL isLoadingCell = (indexPath.section == self.showCount);
    NSString * cellID = [NSString stringWithFormat:@"cellID:%d", isLoadingCell];
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellID];
    if(nil == cell) {
        if(isLoadingCell) {
            // 创建 loading cell
            cell = [[LoadingTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellID];
        }
        else {
            cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier: cellID];
        }
    }
    // loading cell 加载
    if(isLoadingCell) {
        // 设置loading cell 的状态
        [(LoadingTableViewCell *)cell setStatus:self.status];
        // 是否可点击
        [cell setSelectionStyle:((self.status == LoadingStatusDefault) ? UITableViewCellSelectionStyleDefault : UITableViewCellSelectionStyleNone)];
    }
    else {
        NSDictionary *playerData = self.topScorerList[indexPath.section];
        if(indexPath.row == 0) {
            cell.textLabel.text = [NSString stringWithFormat:@"球员:%@", playerData[@"player_name"]];
        }
        else if(indexPath.row == 1) {
            cell.textLabel.text = [NSString stringWithFormat:@"国家:%@", playerData[@"team_name"]];
        }
        else if(indexPath.row == 2) {
            cell.textLabel.text = [NSString stringWithFormat:@"进球数:%@", playerData[@"goals"]];
        }
        else if(indexPath.row == 3) {
            cell.textLabel.text = [NSString stringWithFormat:@"助攻数:%@", playerData[@"assists"]];
        }
    }
    return cell;
}

UITableViewDelegate 协议实现:

-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    NSLog(@"%zd-%zd", indexPath.section, indexPath.row);
    BOOL isLoadingCell = (indexPath.section == self.showCount);
    if(isLoadingCell) {
        self.status = LoadingStatusLoding;
        [tableView reloadData];
        NSLog(@"isloading");
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)1*NSEC_PER_SEC), dispatch_get_main_queue(),^{
            if(self.showCount + 10 <= self.topScorerList.count) {
                self.showCount += 10;
                self.status = LoadingStatusDefault;
            } else {
                self.status = LoadingStatusNoMore;
            }
            [tableView reloadData];
            NSLog(@"load finish");
        });
    }
}

实现效果:

picture

picture

picture


遇到的问题

1.设置界面背景色为自定义RGB颜色

解决方法:

假如要设置背景颜色的RGB为:(51, 204, 255):

// 设置背景颜色
[self.view setBackgroundColor:[UIColor colorWithRed:51/255.0 green:204/255.0 blue:255/255.0 alpha:1]];

2.UI控件如何居中显示

解决方法:

提前计算好位置距离,例如有两个控件横向并排,一起居中显示:

// 控件 UIActivityIndicatorView 宽度
CGFloat indicatorWidth = self.indicatorView.frame.size.width;
// 控件 UILabel 宽度
CGFloat labelWidth = self.tipsLabel.frame.size.width;
// 控件中间间距
CGFloat space = 5;
// 计算好离 parent view 的 左间距 left margin
CGFloat leftMargin = (self.bounds.size.width-indicatorWidth-space-labelWidth)/2;
// 设置控件中心点实现居中
[self.tipsLabel setCenter:CGPointMake(leftMargin + labelWidth/2, self.bounds.size.height/2)];
[self.indicatorView setCenter:CGPointMake(leftMargin + labelWidth + space + indicatorWidth/2, self.bounds.size.height/2)];

3.输入框点击键盘以外的地方,如何收起键盘

解决方法:(重写 touchesBegan 方法)

// 点击键盘以外的地方收起键盘
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    [self.view endEditing:YES];
}

4.导航栏去除导航标题,并解决跳转后无法恢复导航标题的问题

解决方法:重写 viewWillAppear 方法

- (void)viewWillAppear:(BOOL)animated {
    // 隐藏导航栏
    [self.navigationController setNavigationBarHidden:YES animated:YES];
}

5.如何生成一个 Json 格式的 NSData 用于 POST 请求

解决方法:使用 NSDictionary 转换成 NSData

例如要封装的 Json 格式为:

{
    "username":"root",
    "password":"admin"
}
NSDictionary *bodyDict = [[NSDictionary alloc] initWithObjects:@[@"root", @"admin"] forKeys:@[@"username", @"password"]];
    NSData *bodyData = [NSJSONSerialization dataWithJSONObject:bodyDict options:NSJSONWritingPrettyPrinted error:nil];

总结

  • 这一周因为是自习,并没有老师过来上课,所以有时间可以温习巩固一下前面的知识点。
  • 首先开始是尝试纯代码开发重构上一周做的网络访问demo。
  • 然后是学习POST请求,由于之前项目有写一个简单的服务器端,处理登陆注册请求,所以干脆写了一个登陆、注册界面。但由于对IOS的界面View刷新机制还不太熟悉,也有可能是因为虚拟机卡顿的原因导致界面view刷新延迟的bug还未能修复,因此注册功能就暂时未能实现。另外这次是单纯用 SessionRequest 去简单网络访问,下周看看能不能重构使用AFNetWorking 第三方网络访问框架。
  • 然后是学习自定义的 UITableViewCell 实现射手榜一个自定义的Cell样式,实现点击加载更多的功能。初步了解自定义子View过后,打算下周将 球员信息Cell 也进行自定义调整, 球员信息不单单一行行展示出来,而是使用自定义的一个 UITableViewCell 来展示球员头像图片+数据等信息,当然这可能还涉及到 UIImageView 异步加载网络图片的问题,或许可以考虑 SDWebImage 图片加载框架,这又需要进一步去学习。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值