@(iOS 项目实战)[项目实战]
- 作者: Liwx
- 邮箱: 1032282633@qq.com
目录
- 04.项目实战-04 百思不得姐 登录界面补充,我的界面,设置界面清除缓存
- 补充
- 约束和frame并用问题
- 多个对象拥有同样功能,抽取基类
- 代理注意点
- 1.登录/注册界面细节
- 设置UITextField的光标颜色
- 设置UITextField的占位文字颜色
- 自定义文本框类实现修改文本框占位文字颜色和光标颜色
- 设置文本框占位文字颜色方式一
- 设置文本框占位文字颜色方式二
- 设置文本框占位文字颜色方式三
- 2.我的界面搭建
- storyboard搭建我的界面(静态cell)
- 我的界面底部板块界面
- 加载我板块cell网络数据
- 3.展示网页的几种实现方法
- SFSafariViewController实现浏览展示网页(iOS9之后才能使用)
- WKWebView实现展示网页(iOS8之后才能使用)
- 4.设置界面
- 获取SDWebImage缓存大小
- 封装计算文件夹大小/移除文件夹所有文件业务类WXFileCacheManager
- 获取缓存大小原理(获取文件夹下所有文件的大小)
- 移除文件夹下所有文件
补充
约束和frame并用问题
只要修改了约束,就修改约束,如果使用frame,就直接修改frame.如果既用约束,又用frame,有可能会造成冲突.
多个对象拥有同样功能,抽取基类
如果很多对象都有同样的功能,可以考虑抽取基类
代理注意点
不要自己成为自己的代理
,之后如果外部重新设置代理,会导致自己的代理功能失效.
1.登录/注册界面细节
设置UITextField的光标颜色
- UITextField的焦点光标颜色
设置UITextField的主题颜色
tintColor属性
,将tintColor属性设置成白色,光标颜色就为白色.
设置UITextField的占位文字颜色
占位文字实现分析: 占位文字颜色在文本框
开始编辑的时候变成白色,结束编辑的时候恢复原来的颜色
.设置文本框占位文字颜色项目中多处会使用到,避免多处都要进行重复性设置
,建议使用自定义TextField类,将设置占位文字颜色的功能封装到自定义TextField类中.
自定义文本框类实现修改文本框占位文字颜色和光标颜色
- 1.自定义文本框类WXTextField
- 2.在WXTextField类的awakeFormNib方法中设置
tintColor属性
(只需设置一次),实现设置光标颜色.
self.tintColor = [UIColor whiteColor];
- 3.监听文本框开始/结束编辑(开始编辑占位文字颜色为白色,结束编辑占位文字颜色为灰色)
- 此项目使用Target方式监听文本框开始/结束编辑事件.
分析几种监听文本框的方式
- 监听文本框开始/结束编辑方式:代理
,通知
,Target
三种方式.
- 代理方式: 使用代理设置控件内部属性,如果外部重新设置了代理,会导致控件内部通过代理设置的功能失效.此场景不适合用代理方式监听
.
- 通知方式: 使用通知的方式监听文本框开始/结束编辑.开始编辑(UITextFieldTextDidBeginEditingNotification
)通知,结束编辑(UITextFieldTextDidEndEditingNotification
)通知,文本改变(UITextFieldTextDidChangeNotification
)通知.此场景也可以使用通知的方式监听
.
- Target方式: 使用Target方式监听文本框开始/结束编辑.监听文本框的开始编辑事件UIControlEventEditingDidBegin
和结束编辑事件UIControlEventEditingDidEnd
.此场景可以使用Target方式监听
.
- 使用Target方式实现参考源码
// ----------------------------------------------------------------------------
// 在awakeFromNib方法中设置文本框光标颜色和占位文字字体颜色
- (void)awakeFromNib
{
// 1.设置光标颜色
self.tintColor = [UIColor whiteColor];
// 2.添加监听文本框开始编辑和结束编辑
[self addTarget:self action:@selector(textBegin) forControlEvents:UIControlEventEditingDidBegin];
[self addTarget:self action:@selector(textEnd) forControlEvents:UIControlEventEditingDidEnd];
// 3.设置默认占位文字颜色为灰色
// 此处需设置占位文字颜色.
}
#pragma =======================================================================
#pragma mark - 监听文本框事件
// ----------------------------------------------------------------------------
// 监听到文本框开始编辑
- (void)textBegin
{
// 设置开始编辑时占位文字的颜色
}
// ----------------------------------------------------------------------------
// 监听到文本框结束编辑调用
- (void)textEnd
{
// 设置结束编辑时占位文字的颜色
}
- 4.设置占位文字颜色
设置占位文字的场景:
默认情况
的占位文字颜色,开始编辑时
修改占位文字颜色,结束编辑时
恢复占位文字颜色.
设置占位文字颜色分析
- 方式一: 通过修改UITextField的attributedPlaceholder
属性.
- 方法二: 使用KVC方式获取占位文字的UILabel
,获取到Label在设置Label的文字颜色.该方法有前提,必须在设置占位文字颜色前先设置占位文字,因为占位文本Label是使用懒加载,如果使用KVC方式获取占位文本Label时,有可能该Label还没创建,所以使用该方法必须确保占位文本Label已经创建
.
- 方式三: 使用RunTime方式
,通过分类实现设置文本框的占位文字颜色
.
设置文本框占位文字颜色方式一
- 修改UITextField的attributedPlaceholder属性
// ----------------------------------------------------------------------------
// TODO: TextField 设置文本框的占位文字颜色 方法一: 通过修改UITextField的attributedPlaceholder属性
// 在自定义TextField类中使用UITextField的attributedPlaceholder属性修改
- (void)setAttrPlaceholderColor:(UIColor *)placeholderColor
{
// 1.设置富文本属性
NSMutableDictionary *attrDict = [NSMutableDictionary dictionary];
attrDict[NSForegroundColorAttributeName] = placeholderColor;
// 2.创建富文本属性,并设置颜色
NSAttributedString *attributedString = [[NSAttributedString alloc] initWithString:self.placeholder attributes:attrDict];
// 3.将富文本赋值给UITextField的attributedPlaceholder属性
self.attributedPlaceholder = attributedString;
}
设置文本框占位文字颜色方式二
-
断点调试获取到私有成员_placeholderLabel属性名
断点调试获取私有成员_placeholderLabel属性名.png
- 使用KVC方式获取UITextField私有属性_placeholderLabel(占位文字Label),获取到Label在设置Label的文字颜色
注意
:OC系统自带控件中,所有的子控件都是懒加载
.该方法有前提,必须在设置占位文字颜色前先设置占位文字,因为占位文本Label是使用懒加载,如果使用KVC方式获取占位文本Label时,有可能该Label还没创建,所以使用该方法必须确保占位文本Label已经创建
.
// ----------------------------------------------------------------------------
// TODO: TextField 设置文本框的占位文字颜色 方法二: 使用KVC方式获取占位文字的UILabel,获取到Label在设置Label的文字颜色.
- (void)setKVCPlaceholderColor:(UIColor *)placeholderColor
{
// 1.使用KVC获取文本框中的占位文字Label
UILabel *placeholderLabel = [self valueForKeyPath:@"placeholderLabel"];
// 2.设置占位文字Label的颜色
placeholderLabel.textColor = placeholderColor;
}
设置文本框占位文字颜色方式三
- 使用RunTime方式,通过分类设置文本框的占位文字颜色
实现思路分析: 1.先保存占位文字颜色到系统类动态添加的属性. 2.取出动态添加的placeholderColor给系统的占位文字Label设置字体颜色
-
1.
创建UITextField+Placeholder分类
-
2.使用
动态添加placeholderColor属性
,用于存放占位文字的颜色.- 在UITextField+Placeholder.h中声明
@property UIColor *placeholderColor;
,相当于是声明set,get方法. - 实现placeholderColor的set,get方法
set方法中关联动态添加的属性,使用objc_setAssociatedObject函数
关联动态添加的属性.
使用KVC方式获取文本框的占位文字的UILabel,并给占位文字的Label设置文本颜色.
get方法中通过objc_getAssociatedObject函数
获取关联的对象
- 在UITextField+Placeholder.h中声明
-
3.
给系统的设置占位文字的setPlaceholder:方法添加功能
.- 自定义实现
wx_setPlaceholder:方法,该方法实现给系统的setPlaceholder:方法添加设置占位文本颜色的功能
.
- 自定义实现
-
4.交换系统方法: 在load类方法中实现自定义方法和系统方法交换.
- 注意: 自定义的
wx_setPlaceholder:方法
和系统的setPlaceholder:方法
交换,注意别写错要交换的系统方法的setPlaceholder:方法.
- 注意: 自定义的
- RunTime方式实现设置文本框占位文字颜色参考代码
// ----------------------------------------------------------------------------
// TODO: TextField 方式三: 使用RunTime方式,通过分类设置文本框的占位文字颜色
// ----------------------------------------------------------------------------
// UITextField+PlaceholderColor.h文件
#import <UIKit/UIKit.h>
@interface UITextField (Placeholder)
/** 动态添加placeholderColor属性 */
@property UIColor *placeholderColor;
@end
// ----------------------------------------------------------------------------
// UITextField+PlaceholderColor.m文件
#import "UITextField+Placeholder.h"
#import <objc/message.h>
@implementation UITextField (PlaceholderColor)
#pragma =======================================================================
#pragma mark - RunTime实现交换方法
// ----------------------------------------------------------------------------
// 在load类方法中(类加载进内存的时候调用,只调用一次)
+ (void)load
{
// 1.获取要交互的方法 Method是C语言结构体,不需要加*
Method setPlaceholderMethod = class_getInstanceMethod(self, @selector(setPlaceholder:));
Method wx_setPlaceholderMethod = class_getInstanceMethod(self, @selector(wx_setPlaceholder:));
// 2.交换方法
method_exchangeImplementations(setPlaceholderMethod, wx_setPlaceholderMethod);
}
// ----------------------------------------------------------------------------
// 给系统的方法添加设置占位文本颜色的功能
- (void)wx_setPlaceholder:(NSString *)placeholder
{
// 1.调用交互方法,实际是调用setPlaceholderColor:方法
[self wx_setPlaceholder:placeholder];
// 2.设置保存的占位文字颜色
self.placeholderColor = self.placeholderColor;
}
#pragma =======================================================================
#pragma mark - 使用RunTime实现关联动态添加的属性placeholderColor
// ----------------------------------------------------------------------------
// 重写set方法设置占位文本颜色
- (void)setPlaceholderColor:(UIColor *)placeholderColor
{
// 1.关联动态placeholderColor添加的属性,保存占位文字颜色到系统类动态添加的placeholderColor属性
// object:保存到哪个对象中
// key:属性名
// value:属性值
// policy:策略
objc_setAssociatedObject(self, @"placeholderColor", placeholderColor, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
// 2.使用KVC获取占位文字的Label,并设置占位文字Label的字体颜色
UILabel *placeholderLabel = [self valueForKeyPath:@"placeholderLabel"];
placeholderLabel.textColor = placeholderColor;
}
// ----------------------------------------------------------------------------
// 重写get方法获取placeholderColor关联的对象
- (UIColor *)placeholderColor
{
return objc_getAssociatedObject(self, @"placeholderColor");
}
@end
2.我的界面搭建
-
我的界面分析
我的界面分析.png
只有
storyboard才能设置静态单元格
(静态cell),xib不能设置静态单元格
.
storyboard搭建我的界面(静态cell)
-
1.使用storyboard设置好静态单元格
- 需设置
storyboard中控制器的管理类
.
设置storyboard中控制器的管理类.png -
删除系统自动生成的tableView数据源方法代码
.
- 需设置
-
2.
storyboard必须通过代码加载
- 方式一: 加载
箭头指向的控制器instantiateInitialViewController
方法.
// 在WXTabBarController.m中,添加子控制器的方法中 // TODO: Storyboard加载控制器,storyboard必须手动加载控制器 UIStoryboard *storyboard = [UIStoryboard storyboardWithName:NSStringFromClass([WXMeViewController class]) bundle:nil]; WXMeViewController *meVc = [storyboard instantiateInitialViewController];
- 方式二: 加载
指定标识的控制器instantiateViewControllerWithIdentifier:
方法.(在storyboard中必须设置StoryboardID
)
- 方式一: 加载
我的界面底部板块界面
-
UICollectionView的基本使用
- 初始化时必须指定
布局
- 通过布局类
设置cell的尺寸
. - 使用布局
设置最小行列间距
,滚动方法
等.
- 通过布局类
- 必须注册cell
- 必须自定义cell
- 初始化时必须指定
-
tableView的注意点
- 添加到tableView的footView的子控件不需要设置位置尺寸.
- 1.设置底部tableFooterView为UICollectionView
// ----------------------------------------------------------------------------
// 常量和宏
static NSString * const ID = @"cell";
static NSInteger const colCount = 4;
static CGFloat const margin = 1;
#define cellWH ((screenW - margin * (colCount - 1)) / colCount)
// ----------------------------------------------------------------------------
// 设置底部tableFooterView
- (void)setupTableFooterView
{
// ------------------------------------------------------------------------
// 1.创建流水布局,设置cell的尺寸和行列最小间距
UICollectionViewFlowLayout *flowLayout = [[UICollectionViewFlowLayout alloc] init];
flowLayout.itemSize = CGSizeMake(cellWH, cellWH);
flowLayout.minimumLineSpacing = margin;
flowLayout.minimumInteritemSpacing = margin;
// ------------------------------------------------------------------------
// 2.创建collectionView,设置collectionView的属性
UICollectionView *collectionView = [[UICollectionView alloc] initWithFrame:CGRectMake(0, 0, 0, 0) collectionViewLayout:flowLayout];
collectionView.backgroundColor = WXColor(206, 206, 206);
collectionView.scrollEnabled = NO;
self.collectionView = collectionView;
// ------------------------------------------------------------------------
// 3.设置collectionView的数据源和代理
collectionView.dataSource = self;
collectionView.delegate = self;
self.tableView.tableFooterView = collectionView;
// ------------------------------------------------------------------------
// 4.注册cell
[collectionView registerNib:[UINib nibWithNibName:NSStringFromClass([WXSquareCell class]) bundle:nil] forCellWithReuseIdentifier:ID];
}
- 2.设置tableView的分组间距
-
分组间距调整
分组间距调整.png
-
-
3.调整tableView分组间距
- 最上面间距不属于组头部间距
- 调整组尾部间距
- 经过分析,顶部的间距是第一个cell的y值为35,设置内边距左边距为-25,这样就能实现设置最顶部的间距为10;
- 实现参考代码
// ---------------------------------------------------------------------------- // 设置tableView的分组间距 - (void)setSectionMargin { // 设置tableView的分组间距(调整分组的头部尾部间距), 最顶部高度为35,通过设置内边距将最顶部间距设置为10 self.tableView.sectionFooterHeight = 10; self.tableView.sectionHeaderHeight = 0; self.tableView.contentInset = UIEdgeInsetsMake(-25, 0, 0, 0); }
加载我板块cell网络数据
-
1.使用AFN加载板块cell网络数据
- 1.创建请求会话管理者
- 2.拼接请求参数(必选参数一定要写)
- 3.发送请求
- 请求板块cell数据参考代码
// ----------------------------------------------------------------------------
// 请求cell网络数据
- (void)loadData
{
// 1.创建请求会话管理者
AFHTTPSessionManager *mgr = [AFHTTPSessionManager manager];
// 2.拼接请求参数
NSMutableDictionary *parameters = [NSMutableDictionary dictionary];
parameters[@"a"] = @"square";
parameters[@"c"] = @"topic";
// 3.发送请求
[mgr GET:baseUrl parameters:parameters progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
// 3.1 字典数组转模型数组
self.squareList = [WXSquareItem mj_objectArrayWithKeyValuesArray:responseObject[@"square_list"]];
// 3.2 处理请求的数据
[self resolveData];
// 3.3 刷新表格
[self.collectionView reloadData];
// 3.4 计算collectView的高度
NSInteger count = self.squareList.count;
// 3.4.1 计算行数
NSInteger row = (count - 1) / colCount + 1;
CGFloat collectionViewH = row * cellWH;
self.collectionView.wx_height = collectionViewH;
// TODO: 3.5 重新设置tableFooterView的显示内容,如果重新设置,会导致拖动到最底部cell会自动回弹
self.tableView.tableFooterView = self.collectionView;
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
NSLog(@"%@", error);
}];
}
- 2.处理请求的数据
作用: 主要是用于
补齐格子
,不然未填充的格子会变成灰色
的效果.
- 处理请求的数据实现代码
// ----------------------------------------------------------------------------
// 处理请求的数据
- (void)resolveData
{
// 如果数据不是4的倍数,添加到4的倍数.主要是用于补齐格子,不然未填充的格子会变成灰色的效果.
NSInteger count = self.squareList.count;
NSInteger extre = count % colCount;
if (extre) {
for (NSInteger i = 0; i < colCount - extre ; i++) {
WXSquareItem *item = [[WXSquareItem alloc] init];
[self.squareList addObject:item];
}
}
}
-
3.使用xib自定义UICollectionViewCell.(WXSquareCell)
-
使用AutoLayout布局WXSquareCell.(适配不同屏幕)布局视图
使用AutoLayout布局WXSquareCell.(适配不同屏幕)布局视图.png
- 设置图片和文字: 提供模型数据,重写set方法
// ---------------------------------------------------------------------------- // 重写模型的set方法,设置图片和文字 - (void)setItem:(WXSquareItem *)item { _item = item; [self.iconImageView sd_setImageWithURL:[NSURL URLWithString:item.icon]]; self.nameLabel.text = item.name; }
- 实现点击cell时闪烁效果
// ---------------------------------------------------------------------------- // WXSquareCell.m文件 - (void)awakeFromNib { // 设置UICollectionViewCell选中时的背景色 UIView *backView = [[UIView alloc] init]; backView.backgroundColor = WXColor(206, 206, 206); self.selectedBackgroundView = backView; } // ---------------------------------------------------------------------------- // WXMeViewController.m文件 - (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath { // 1.处理点击闪烁 UICollectionViewCell *cell = [collectionView cellForItemAtIndexPath:indexPath]; cell.selected = YES; dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ cell.selected = NO; }); // 点击跳转网页处理 }
-
3.展示网页的几种实现方法
展示网页: 1.WebView 2.openUrl
- WebView: 没有自带功能.好处: 就在当前应用下展示网页
.弊端:webView不能监听进度条
.
- safari:自带了很多功能
,弊端:必须要跳转到其他应用
.
SFSafariViewController实现浏览展示网页(iOS9之后才能使用)
- SFSafariViewController实现展示网页实现参考代码
// 使用SFSafariViewController需导入SafariServices/SafariServices.h头文件
#import <SafariServices/SafariServices.h>
// --------------------------------------------------------------------
// TODO: SFSafariViewController实现浏览展示网页
// 3.1 创建Safari网页控制器 iOS9才能用
SFSafariViewController *safariVc = [[SFSafariViewController alloc] initWithURL:[NSURL URLWithString:item.url]];
safariVc.delegate = self;
// 3.2 跳转网页控制器
[self presentViewController:safariVc animated:YES completion:nil];
safariVc.hidesBottomBarWhenPushed = YES;
[self.navigationController pushViewController:safariVc animated:YES];
- SFSafariViewControllerDelegate代理方法
#pragma =======================================================================
#pragma mark - SFSafariViewControllerDelegat代理协议
// ----------------------------------------------------------------------------
// TODO: SFSafariViewController 监听Safari点击完成按钮
- (void)safariViewControllerDidFinish:(SFSafariViewController *)controller;
// ----------------------------------------------------------------------------
// TODO: SFSafariViewController 监听Safari初始化载入完成
- (void)safariViewController:(SFSafariViewController *)controller didCompleteInitialLoad:(BOOL)didLoadSuccessfully;
WKWebView实现展示网页(iOS8之后才能使用)
-
WebKit
框架的WKWebView展示网页
(iOS8
之后才能使用)- 在工程文件中手动
导入WebKit框架
- 使用WKWebView需先导入库WebKit库,否则会报错
Undefined symbols for architecture x86_64:
"OBJC_CLASS$_WKWebView", referenced from:
工程文件中手动导入WebKit框架 .png
- 使用WKWebView需先导入库WebKit库,否则会报错
- KVO监听前进后退刷新等属性变化
- KVO监听加载进度.
- KVO监听网页标题改变.
- 在工程文件中手动
-
自定义WKWebView
-
1.使用xib布局网页展示界面
使用xib布局网页展示界面.png- 在viewDidLayoutSubviews方法中重写设置WKWebView的尺寸
// ---------------------------------------------------------------------------- // 控制器的view子控件布局完成调用 - (void)viewDidLayoutSubviews { [super viewDidLayoutSubviews]; // 1.设置wkWebView位置尺寸 self.wkWebView.frame = self.htmlView.bounds; }
- 2.提供一个url,让外界传递要展示的url
- 3.添加WXWebView到控制器的view,使用WKWebView的
loadRequest:方法
加载网页
// 通过传递的参数加载网页 NSURLRequest *request = [NSURLRequest requestWithURL:self.url]; [wkWebView loadRequest:request];
- 4.使用KVO添加监听后退,前进,进度条,网页标题的改变
#pragma ======================================================================= #pragma mark - 监听后退,前进,进度条,网页标题的监听 // ---------------------------------------------------------------------------- // 使用KVO添加监听 - (void)setupAddObserver { // 1.添加后退,前进,进度条,网页标题的监听 [self.wkWebView addObserver:self forKeyPath:@"canGoBack" options:NSKeyValueObservingOptionNew context:nil]; [self.wkWebView addObserver:self forKeyPath:@"canGoForward" options:NSKeyValueObservingOptionNew context:nil]; [self.wkWebView addObserver:self forKeyPath:@"estimatedProgress" options:NSKeyValueObservingOptionNew context:nil]; [self.wkWebView addObserver:self forKeyPath:@"title" options:NSKeyValueObservingOptionNew context:nil]; } // ---------------------------------------------------------------------------- // 观察的属性有新值的时候就会调用 - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context { // 1.更新后退,前进,进度条,网页标题的监听 self.backItem.enabled = _wkWebView.canGoBack; self.forwardItem.enabled = _wkWebView.canGoForward; self.progressView.progress = _wkWebView.estimatedProgress; self.progressView.hidden = _wkWebView.estimatedProgress >= 1; self.title = _wkWebView.title; } // ---------------------------------------------------------------------------- // 移除KVO监听, 如果没移除KVO监听,点击返回会报错"NSKeyValueObservationInfo" - (void)dealloc { [self.wkWebView removeObserver:self forKeyPath:@"canGoBack"]; [self.wkWebView removeObserver:self forKeyPath:@"canGoForward"]; [self.wkWebView removeObserver:self forKeyPath:@"estimatedProgress"]; [self.wkWebView removeObserver:self forKeyPath:@"title"]; }
- 4.点击前进,后退,刷新按钮执行相应操作
- WKWebView的
前进goForward方法
,后退goBack方法
,刷新reload方法
,
- WKWebView的
#pragma ======================================================================= #pragma mark - 监听后退,前进,刷新按钮的点击调用方法 // ---------------------------------------------------------------------------- // 后退按钮点击调用 - (IBAction)back:(UIBarButtonItem *)sender { [self.wkWebView goBack]; } // ---------------------------------------------------------------------------- // 前进按钮点击调用 - (IBAction)forward:(UIBarButtonItem *)sender { [self.wkWebView goForward]; } // ---------------------------------------------------------------------------- // 刷新按钮点击调用 - (IBAction)reload:(UIBarButtonItem *)sender { [self.wkWebView reload]; }
-
4.设置界面
获取SDWebImage缓存大小
-
SDWebImage获取的图片存放在Cache/default,获取该目录的缓存大小
// 获取SDWebImage缓存大小 NSUInteger cacheSize = [[SDImageCache sharedImageCache] getSize];
封装计算文件夹大小/移除文件夹所有文件业务类WXFileCacheManager
业务类: 常见业务类有
网络请求类
,处理文件
缓存类.
业务类命名: 类名可以以manager结尾
.
获取缓存大小原理(获取文件夹下所有文件的大小)
-
获取文件夹的内容属于比较耗时的操作,最好
放在异步子线程处理
,避免导致UI操作卡顿.以下所有操作都放在异步子线程中执行
. -
封装获取文件夹大小的类方法
-
1.使用
[NSFileManager defaultManager];
获得文件管理者,NSFileManager是单例
. -
2.判断路径参数是否存在并且是文件夹
- 如果路径不存在或不是文件夹, 抛出异常
-
3.获取文件夹下的所有文件子路径
- 调用NSFileManager 的对象方法subpathsAtPath:方法获取文件夹下所有文件的子路径
-
4.遍历文件夹下的所有文件,累计文件夹下的所有文件大小
- 4.1 拼接全路径
- 4.2 过滤隐藏文件和文件夹: 判断如果是隐藏文件带有DS字符串,路径不存在或不是文件夹路径,则跳过
- 4.3 获取文件属性,获取文件大小
*NSDictionary attr = [mgr attributesOfItemAtPath:fullPath error:nil];
NSInteger fileSize = [attr[NSFileSize] integerValue]; - 4.4 累计所有文件大小
-
5.执行计算完成后要处理的操作.执行block.
因计算文件夹大小在异步子线程中执行,为了获得异步子线程计算文件夹大小的结果,可以让封装的方法传入一个block参数,让子线程调用block方法,并将计算结果传递给外界.如果block内部有刷新UI操作,必须在主线程调用block.
异步方法不需要设置返回值
.
- 主线程回调执行block参考代码
```objectivec
dispatch_async(dispatch_get_main_queue(), ^{
if (completeBlock) {
completeBlock(totalSize);
}
});
```
- 获取文件夹内所有文件大小参考代码
// ----------------------------------------------------------------------------
// TODO: 获取文件夹大小
+ (void)getCacheSizeOfDirectoriesPath:(NSString *)directoriesPath completeBlock:(void(^)(NSInteger))completeBlock
{
// ------------------------------------------------------------------------
// 异步(子线程)计算文件大小,此操作比较耗时,所以放在子线程计算
dispatch_async(dispatch_get_global_queue(0, 0), ^{
// 1.创建文件管理者
NSFileManager *mgr = [NSFileManager defaultManager];
// TODO: 2.判断是否是文件夹
BOOL isDirectory;
// 返回值: 路径是否存在, isDirectory: 输出是否是文件夹(目录)
BOOL isExists = [mgr fileExistsAtPath:directoriesPath isDirectory:&isDirectory];
// 如果路径不存在或不是文件夹, 抛出异常
if (!isExists || !isDirectory) {
NSException *exp = [NSException exceptionWithName:@"directoriesPathError" reason:@"directoriesPath must be directory" userInfo:nil];
[exp raise];
}
// 3.获取文件夹下的所有文件子路径
NSArray *subPathArray = [mgr subpathsAtPath:directoriesPath];
// --------------------------------------------------------------------
// 4.遍历文件夹下的所有文件,累计文件夹下的所有文件大小
NSInteger totalSize = 0;
for (NSString *subPath in subPathArray) {
// 4.1 拼接全路径
NSString *fullPath = [directoriesPath stringByAppendingPathComponent:subPath];
// 4.2 过滤隐藏文件和文件夹: 判断如果是隐藏文件带有DS字符串,路径不存在或不是文件夹路径,则跳过
isExists = [mgr fileExistsAtPath:fullPath isDirectory:&isDirectory];
if ([fullPath containsString:@"DS"] || !isExists || isDirectory) {
continue;
}
// 4.3 获取文件属性
NSDictionary *attr = [mgr attributesOfItemAtPath:fullPath error:nil];
// 4.4 获取文件大小
NSInteger fileSize = [attr[NSFileSize] integerValue];
// 4.5 累计文件大小
totalSize += fileSize;
}
// --------------------------------------------------------------------
// 执行主线程回调,因为block有刷新UI操作,所以必须在主线程执行
dispatch_async(dispatch_get_main_queue(), ^{
if (completeBlock) {
completeBlock(totalSize);
}
});
});
}
移除文件夹下所有文件
- 移除文件夹下所有文件的类方法
- 移除文件夹的内容属于比较耗时的操作,最好
放在异步子线程处理
,避免导致UI操作卡顿.以下所有操作都放在异步子线程中执行
. - 1.使用
[NSFileManager defaultManager];
获得文件管理者,NSFileManager是单例
. - 2.判断路径参数是否存在并且是文件夹
- 如果路径不存在或不是文件夹, 抛出异常
- 3.获取文件夹下的所有文件子路径
- 调用NSFileManager 的对象方法subpathsAtPath:方法获取文件夹下所有文件的子路径
- 4.遍历文件夹下的所有文件,累计文件夹下的所有文件大小
- 4.1 拼接全路径
- 4.2 移除文件
- 5.执行移除文件完成后要处理的操作.执行block.
- 移除文件夹下所有文件实现参考代码
// ----------------------------------------------------------------------------
// TODO: 移除文件夹路径下的所有文件
+ (void)removeDirectoriesPath:(NSString *)directoriesPath completeBlock:(void(^)())completeBlock
{
// ------------------------------------------------------------------------
// 异步(子线程)移除,此操作比较耗时,所以放在子线程执行
dispatch_async(dispatch_get_global_queue(0, 0), ^{
// 1.创建文件管理者
NSFileManager *mgr = [NSFileManager defaultManager];
// 2.判断是否是文件夹
BOOL isDirectory;
BOOL isExists = [mgr fileExistsAtPath:directoriesPath isDirectory:&isDirectory];
if (!isExists || !isDirectory) {
NSException *exp = [NSException exceptionWithName:@"directoriesPathError" reason:@"directoriesPath must be directory" userInfo:nil];
[exp raise];
}
// 3.获取路径下所有子路径
NSArray *subPathArray = [mgr subpathsAtPath:directoriesPath];
// 4.遍历移除文件夹下的所有文件
for (NSString *subPath in subPathArray) {
// 4.1 拼接全路径
NSString *fullPath = [directoriesPath stringByAppendingPathComponent:subPath];
// 4.2 移除文件
[mgr removeItemAtPath:fullPath error:nil];
}
// 5.执行移除文件完成后要处理的操作.避免block里有执行UI操作,最好在主线程下执行
dispatch_async(dispatch_get_main_queue(), ^{
if (completeBlock) {
completeBlock();
}
});
});
}
网友评论