一 自定义cell–xib
- cell分割线
- 设置透明度可以使分割线变细
- 自定义分割线
- 方法一:去掉系统cell分割线,建一个高度为1的view当分割线;
self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
- 方法二:改变cell的高度-1,但是位置不变
- 存在问题:手动修改完cell的frame,可能还会被系统改回去
- 解决方案:在系统设置完cell的frame后,再手动修改cell的frame
/**
* 重写这个方法的目的:拦截cell的frame设置
*/
- (void)setFrame:(CGRect)frame
{
frame.size.height -= 1;
// 设置左右加间距
frame.origin.x += 5;
[super setFrame:frame];
}
1.重写setFrame,控件frame永远不能修改
2.重写setBounds,控件bounds一样不能修改
二 显示标签数据
- 加载标签数据
// 加载标签数据
// 请求参数
NSMutableDictionary *params = [NSMutableDictionary dictionary];
params[@"a"] = @"tag_recommend";
params[@"action"] = @"sub";
params[@"c"] = @"topic";
// 发送请求
// 发送请求
[[AFHTTPSessionManager manager] GET:@"http://api.budejie.com/api/api_open.php" parameters:params success:^(NSURLSessionDataTask *task, id responseObject) {
// 将服务器的数据写成plist。方便查看数据结构
// [responseObject writeToFile:@"/Users/lujing/Desktop/tag.plist" atomically:YES];
// responseObject:字典数组
// self.tags:模型数组
// responseObject -> self.tags
self.tags = [XMGTag objectArrayWithKeyValuesArray:responseObject];
// 刷新表格
[self.tableView reloadData];
} failure:^(NSURLSessionDataTask *task, NSError *error) {
}];
- cell模型样式代码
- 如果订阅数量比较大,显示x.xx万
#import <UIImageView+WebCache.h>
- (void)setTagModel:(XMGTag *)tagModel
{
_tagModel = tagModel;
[self.imageListView sd_setImageWithURL:[NSURL URLWithString:tagModel.image_list] placeholderImage:[UIImage imageNamed:@"defaultUserIcon"]];
self.themeNameLabel.text = tagModel.theme_name;
// 订阅数
if (tagModel.sub_number >= 10000) {
self.subNumberLabel.text = [NSString stringWithFormat:@"%.1f万人订阅", tagModel.sub_number / 10000.0];
} else {
self.subNumberLabel.text = [NSString stringWithFormat:@"%zd人订阅", tagModel.sub_number];
}
}
三 请求细节处理
- 代码模拟网络比较慢的情况,加载过程
- SVProgressHUD.h第三方框架
- 加载数据,弹窗
[SVProgressHUD show];
// 同样功能,如果没有返回请求,用户没法回到上一view
// [SVProgressHUD showWithMaskType:SVProgressHUDMaskTypeBlack];
- 2种情况下,会显示加载标签数据失败
- 发送一个空的响应
- 发送地址不正确
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
// 发送请求
[AFHTTPSessionManager manager] GET:@"http://api.budejie.com/api/api_open.php"parameters:params success:^(NSURLSessionDataTask *task, id responseObject) {
if (responseObject == nil) {
// 关闭弹框
[SVProgressHUD showErrorWithStatus:@"加载标签数据失败"];
return;
}
// responseObject:字典数组
// weakSelf.tags:模型数组
// responseObject -> weakSelf.tags
self.tags = [XMGTag objectArrayWithKeyValuesArray:responseObject];
// 刷新表格
[weakSelf.tableView reloadData];
// 关闭弹框
[SVProgressHUD dismiss];
} failure:^(NSURLSessionDataTask *task, NSError *error) {
// 如果是取消了任务,就不算请求失败,就直接返回
if (error.code == NSURLErrorCancelled) return;
if (error.code == NSURLErrorTimedOut) {
// 关闭弹框
[SVProgressHUD showErrorWithStatus:@"加载标签数据超时,请稍后再试!"];
} else {
// 关闭弹框
[SVProgressHUD showErrorWithStatus:@"加载标签数据失败"];
}
}];
- 加载数据弹窗用[SVProgressHUD show]时,返回弹窗仍存在
- 方法1:控制器挂掉时,弹窗消失
- (void)dealloc
{
[SVProgressHUD dismiss];
}
- 方法2:view即将消失时,弹窗消失
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
[SVProgressHUD dismiss];
}
- block中使用self需要使用弱引用
- block交由manager管理,manager内部有字典组成,manager不死,block就不会死,由于block对self有一强引用,所以self也不会死
- block什么时候死?当请求完毕,调用完block时
- 原始需求实现点击返回,弹窗消失,__weak typeof(self) weakSelf = self;
控制器关掉,需要停止请求
方法1:数组所有任务执行cancel方法,相当于for循环
[self.manager.tasks makeObjectsPerformSelector:@selector(cancel)];
方法2:调用get方法,多个任务的话使用不方便
/** 任务 */ @property (nonatomic, weak) NSURLSessionTask *task; [self.task cancel];
方法3 :所有任务取消掉
[self.manager invalidateSessionCancelingTasks:YES];
方法1和3区别
- 方法1manager还可以用,相当于加载tasks时把之前请求取消,在发送一个新的请求;
- 方法3把整个manager中session都干掉,在启用只能把manager清掉,在创建一个新的
通过error判断请求是取消导致失败还是真的请求失败
- NSURLErrorCancelled,error有一个code属性
- kCFURLErrorCancelled = -999, 取消
- kCFURLErrorTimedOut = -1001, 超时
- kCFURLErrorUnsupportedURL = -1002, URL有误
- kCFURLErrorCannotFindHost = -1003, 找不带服务器
- kCFURLErrorCannotConnectToHost = -1004,
- NSURLErrorCancelled,error有一个code属性
总结
1>block里面不写self;
2>建议把manager存起来,方便管理所有请求;
3>如果服务器返回对象是空的,增加判断处理
4>failure的block做事情时,判断是取消任务还是真的有错误
5>控制器挂掉一定要取消请求
弱指针注意点:属性只要加了IBOutlet,系统会有隐藏的强引用引用着对象;
只要是弱指针,最终都会死掉,死的时刻不一样;
四 圆角图片
- 方法1:layer图层,如果使用过于频繁,会导致拖拽起来的感觉比较卡
- (void)awakeFromNib
{
self.imageListView.layer.cornerRadius = self.imageListView.width * 0.5;
self.imageListView.layer.masksToBounds = YES;
}
- 方法2:裁剪
- (void)setTagModel:(XMGTag *)tagModel
{
_tagModel = tagModel;
UIImage *placeholder = [[UIImage imageNamed:@"defaultUserIcon"] circleImage];
[self.imageListView sd_setImageWithURL:[NSURL URLWithString:tagModel.image_list] placeholderImage:placeholder completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) {
// 如果图片下载失败,就不做任何处理,按照默认的做法:会显示占位图片
if (image == nil) return;
// 开启图形上下文
UIGraphicsBeginImageContext(image.size);
// 获得上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
// 矩形框
CGRect rect = CGRectMake(0, 0, image.size.width, image.size.height);
// 添加一个圆
CGContextAddEllipseInRect(ctx, rect);
// 裁剪(裁剪成刚才添加的图形形状)
CGContextClip(ctx);
// 往圆上面画一张图片
[self drawInRect:rect];
// 获得上下文中的图片
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
// 关闭图形上下文
UIGraphicsEndImageContext();
}];
}
- 封装一个图片裁圆的分类 “UIImage+XMGExtension.h”
/**
* 返回一张圆形图片
*/
- (instancetype)circleImage
{
// 开启图形上下文
UIGraphicsBeginImageContext(self.size);
// 获得上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
// 矩形框
CGRect rect = CGRectMake(0, 0, self.size.width, self.size.height);
// 添加一个圆
CGContextAddEllipseInRect(ctx, rect);
// 裁剪(裁剪成刚才添加的图形形状)
CGContextClip(ctx);
// 往圆上面画一张图片
[self drawInRect:rect];
// 获得上下文中的图片
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
// 关闭图形上下文
UIGraphicsEndImageContext();
return image;
}
+ (instancetype)circleImageNamed:(NSString *)name
{
return [[self imageNamed:name] circleImage];
}
五 封装圆角头像
- 原始需求:项目中所有图片都可以裁剪成圆形
- 新建分类 “UIImageView+XMGExtension.h”
- (void)setCircleHeader:(NSString *)url
{
XMGWeakSelf;
UIImage *placeholder = [[UIImage imageNamed:@"defaultUserIcon"] circleImage];
[self sd_setImageWithURL:[NSURL URLWithString:url] placeholderImage:placeholder completed:
^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) {
// 如果图片下载失败,就不做任何处理,按照默认的做法:会显示占位图片
if (image == nil) return;
weakSelf.image = [image circleImage];
}];
}
六 全局常量和全局变量
- 全局常量:宏,定义全局常量,不能修改,会产生很多临时空间;
- 全局变量:可以修改,不会产生临时空间;
折中办法:选择const
- const : const只修饰它右边的内容,被const修饰的内容都是常量、都是不能再修改的;
- static : 被static修饰的全局变量\常量
- 1) 仅限于当前文件访问
- 2) 改变了作用域
static : 被static修饰的局部变量
- 1) 只会占用一块内存,在整个程序运行过程都不会销毁,只会初始化一次
- 2) 改变了生命周期,并没有改变作用域
extern : 可以引用一个全局变量\常量(注意重复定义)
增加全局常量方法
- 仅限本文件中访问
- 本文件(.m)中写代码:staic 类型 const 常量名 = 常量值;
- 全世界都要访问
- .h文件引用全局常量 : UIKIT_EXTERN 类型 const 常量名;
- .m文件定义全局常量的值 : 类型 const 常量名 = 常量值;
- 在pch中包含.h文件
- 仅限本文件中访问