04.项目实战 百思不得姐 登录界面补充,我的界面,设置界面清除缓存

@(iOS 项目实战)[项目实战]


目录

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

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,555评论 18 139
  • 一 、使用git管理工程 1、可以使用OSChina远程管理工程(免费) 2、可以使用GitHub网站进行远程管理...
    YuGHo阅读 2,331评论 1 3
  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 11,973评论 4 60
  • @(iOS 项目实战)[项目实战] 作者: Liwx 邮箱: 1032282633@qq.com 目录 05.项目...
    Liwx阅读 890评论 0 2
  • 有人愿意为了爱飞蛾扑火。我却觉得这是自取灭亡
    veve柯殿阅读 109评论 0 0