IOS 压缩与解压字符串(文件) 操作

176 阅读11分钟

1、压缩与解压字符串(文件) 操作

实现对字符串进行压缩解压操作  前提需要导入libz.1.2.5.dylib 库

#import "BYViewController.h"
#import "zipAndUnzip.h"
 
@interface BYViewController ()
 
@end
 
@implementation BYViewController
 
- (void)viewDidLoad
{
    [super viewDidLoad];
   
 zipAndUnzip *zipAnd = [[zipAndUnzip alloc] init];
    
    NSString *str = @"Hello.madison向显笑";
    NSStringEncoding enc = CFStringConvertEncodingToNSStringEncoding(NSUTF16BigEndianStringEncoding);
    NSData *data = [str dataUsingEncoding:enc];
    NSData *dataDeflate = [zipAnd gzipDeflate:data];
    Byte *byte = (Byte *)[dataDeflate bytes];
 
    for (int i=0 ; i<[dataDeflate length]; i++){
    
        NSLog(@"byte = %d",byte[i]);
    }
    
    NSData *adata = [[NSData alloc] initWithBytes:byte length:[dataDeflate length]];
//    NSData *dataInflate =  [zipAnd gzipInflate:adata];
    NSString *aString = [[NSString alloc] initWithData:adata encoding:enc];
    NSLog(@"aString:%@",aString);
 
}
 
@end
@interface zipAndUnzip : NSObject
 
- (NSData *)gzipInflate:(NSData*)data;
- (NSData *)gzipDeflate:(NSData*)data;
 
@end
#import "zipAndUnzip.h"
#import "zlib.h"
 
@implementation zipAndUnzip
 
//解压缩
- (NSData *)gzipInflate:(NSData*)data
{
    if ([data length] == 0) return data;
    
    unsigned full_length = (int)[data length];
    unsigned half_length = (int)[data length] / 2;
    
    NSMutableData *decompressed = [NSMutableData dataWithLength: full_length + half_length];
    BOOL done = NO;
    int status;
    
    z_stream strm;
    strm.next_in = (Bytef *)[data bytes];
    strm.avail_in = (int)[data length];
    strm.total_out = 0;
    strm.zalloc = Z_NULL;
    strm.zfree = Z_NULL;
    
    if (inflateInit2(&strm, (15+32)) != Z_OK)
        return nil;
    
    while (!done)
    {
        // Make sure we have enough room and reset the lengths.
        if (strm.total_out >= [decompressed length])
            [decompressed increaseLengthBy: half_length];
        strm.next_out = [decompressed mutableBytes] + strm.total_out;
        strm.avail_out = (uint)[decompressed length] - (uint)strm.total_out;
        
        // Inflate another chunk.
        status = inflate (&strm, Z_SYNC_FLUSH);
        if (status == Z_STREAM_END)
            done = YES;
        else if (status != Z_OK)
            break;
    }
    if (inflateEnd (&strm) != Z_OK)
        return nil;
    
    // Set real length.
    if (done)
    {
        [decompressed setLength: strm.total_out];
        return [NSData dataWithData: decompressed];
    }
    else return nil;
}
 
 
//压缩
- (NSData *)gzipDeflate:(NSData*)data
{
    if ([data length] == 0) return data;
    
    z_stream strm;
    
    strm.zalloc = Z_NULL;
    strm.zfree = Z_NULL;
    strm.opaque = Z_NULL;
    strm.total_out = 0;
    strm.next_in=(Bytef *)[data bytes];
    strm.avail_in = (int)[data length];
    
    // Compresssion Levels:
    //   Z_NO_COMPRESSION
    //   Z_BEST_SPEED
    //   Z_BEST_COMPRESSION
    //   Z_DEFAULT_COMPRESSION
    
    if (deflateInit2(&strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED, (15+16), 8, Z_DEFAULT_STRATEGY) != Z_OK) return nil;
    
    NSMutableData *compressed = [NSMutableData dataWithLength:16384];  // 16K chunks for expansion
    
    do {
        
        if (strm.total_out >= [compressed length])
            [compressed increaseLengthBy: 16384];
        
        strm.next_out = [compressed mutableBytes] + strm.total_out;
        strm.avail_out = (uInt)[compressed length] - (uint)strm.total_out;
        
        deflate(&strm, Z_FINISH);
        
    } while (strm.avail_out == 0);
    
    deflateEnd(&strm);
    
    [compressed setLength: strm.total_out];
    return [NSData dataWithData:compressed];
}
 
@end

2、ZIP、RAR、7Z文件解压缩

引用第三方库:

pod 'SSZipArchive'

pod "UnrarKit"

pod 'LzmaSDK-ObjC'

控制器实现

.h 头文件控制器定义:

//
//  ZipRarViewController.h
//
//  Created by carbonzhao on 2024/5/28.
//
 
#import "IMBaseClassViewController.h"
 
NS_ASSUME_NONNULL_BEGIN
 
@interface ZipRarViewController : IMBaseClassViewController
 
//可为本地路径,亦可为远端路径
@property (nonatomic,strong) NSURL *filePath;
 
//路径标记,相同标记解压过的不再解压
@property (nonatomic,strong) NSString *fileFlag;
 
@end
 
NS_ASSUME_NONNULL_END

.m 文件实现:

//
//  ZipRarViewController.m
//  QGB_IM2
//
//  Created by carbonzhao on 2024/5/28.
//
 
#import "ZipRarViewController.h"
#import "SSZipArchive.h"
#import "UIBreadView.h"
#import "ZipRar7zUnArchive.h"
 
NS_ASSUME_NONNULL_BEGIN
 
@interface UINextTreeView : UIView
{
}
- (void)setDataSource:(NSMutableArray*)list checkActionBlock:(void (^)(NSDictionary *e))checkActionBlock;
@end
 
NS_ASSUME_NONNULL_END
 
typedef void (^SubTreeViewCheckedTreeBlock)(NSDictionary *e);
 
@interface UINextTreeView ()<UITableViewDelegate,UITableViewDataSource>
{
    NSMutableArray *dataList;
    UITableView *tview;
}
@property (nonatomic,copy) SubTreeViewCheckedTreeBlock ClickTreeBlock;
@end
 
@implementation UINextTreeView
 
- (instancetype)initWithFrame:(CGRect)frame
{
    if (self = [super initWithFrame:frame])
    {
        [self setupUI];
    }
    return self;
}
 
 
- (void)setFrame:(CGRect)frame
{
    [super setFrame:frame];
    if (tview)
    {
        [tview setFrame:CGRectMake(0, 0, frame.size.width, frame.size.height)];
    }
}
 
 
- (void)setupUI
{
    dataList = [[NSMutableArray alloc] initWithCapacity:0];
    
    tview = [[UITableView alloc] initWithFrame:self.bounds];
    tview.backgroundColor = DSTextPlaceColor;
    tview.dataSource = self;
    tview.delegate = self;
//    tview.tableFooterView = [UIView new];
    tview.separatorColor = [UIColor clearColor];
    tview.estimatedRowHeight = 0;
    tview.estimatedSectionFooterHeight = 0;
    tview.estimatedSectionHeaderHeight = 0;
    [tview setShowsVerticalScrollIndicator:NO];
    [tview setShowsHorizontalScrollIndicator:NO];
//    [tview setBackgroundColor:[UIColor redColor]];
//    tview.mj_header = [MJRefreshNormalHeader headerWithRefreshingBlock:^{
//        [weakSelf getListAllDing:@"refresh"];
//    }];
    
    [self addSubview:tview];
}
 
#pragma mark - action
- (void)setDataSource:(NSMutableArray*)list checkActionBlock:(void (^)(NSDictionary *e))checkActionBlock
{
    [self setClickTreeBlock:checkActionBlock];
    [dataList addObjectsFromArray:list];
    [tview reloadData];
}
 
 
- (void)reloadView
{
    dispatch_async(dispatch_get_main_queue(), ^{
        [self->tview reloadData];
    });
}
 
 
- (NSString *)docIcon:(NSString *)ext
{
    NSString *ex = [ext lowercaseString];
    if ([@[@"xls",@"xlsx"] containsObject:ex])
    {
        return @"icon_excel_new";
    }
    else if ([@[@"doc",@"docx"] containsObject:ex])
    {
        return @"icon_word_new";
    }
    else if ([@[@"ppt",@"pptx"] containsObject:ex])
    {
        return @"icon_ppt_new";
    }
    else if ([@[@"pdf"] containsObject:ex])
    {
        return @"icon_pdf_new";
    }
    else if ([@[@"txt"] containsObject:ex])
    {
        return @"icon_txt_new";
    }
    else if ([@[@"png",@"jpg",@"jpeg"] containsObject:ex])
    {
        return @"icon_img_new";
    }
    else if ([@[@"mov",@"mp4",@"avi",@"mpeg",@"mkv",@"3gp",@"wmv",@"rmvb"] containsObject:ex])
    {
        return @"icon_mp4_new";
    }
    else if ([@[@"mp3",@"wav"] containsObject:ex])
    {
        return @"icon_mp3";
    }
    else if ([@[@"zip",@"rar",@"7z"] containsObject:ex])
    {
        return @"icon_zip_new";
    }
    return @"icon_txt_new";
}
 
#pragma mark - tableView
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    return 1;
}
 
 
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return dataList.count;
}
 
 
 
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    CGFloat height = 58;
    return height;
}
 
 
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:nil];
    CGFloat height = [self tableView:tableView heightForRowAtIndexPath:indexPath];
    if (height > 0)
    {
        CGFloat x = 20;
        NSMutableDictionary *e = [dataList objectAtIndex:indexPath.row];
        
        kWeakSelf(self)
        NSString *dicon = [e boolForKey:@"isDir"]?@"docshare":[self docIcon:[e stringForKey:@"ext"]];
        CGRect rt = CGRectMake(x, (height-30)/2, 30, 30);
        UIImageView *dirIcon = [[UIImageView alloc] initWithFrame:rt];
        [dirIcon setImage:IMAGE_INIM_NAME(dicon)];
        [dirIcon setContentMode:UIViewContentModeScaleAspectFit];
        [cell.contentView addSubview:dirIcon];
        
        x += (rt.size.width + 10);
        NSString *name = [e stringForKey:@"fileName"];
        UIFont *s = Font_size(16);
        UIFont *t = Font_size(13);
        rt = CGRectMake(x, (height-4-s.lineHeight-t.lineHeight)/2, tableView.frame.size.width-20-x-40, s.lineHeight);
        UILabel *label = [[UILabel alloc] initWithFrame:rt];
        [label setFont:s];
        [label setTextColor:RGBA(10, 10, 10, 1)];
        [label setText:name];
        [label setTextAlignment:NSTextAlignmentLeft];
        [cell.contentView addSubview:label];
        
        NSString *subName = [e stringForKey:@"fileCount"];
        rt.origin.y += rt.size.height + 4;
        rt.size.width = 140;
        label = [[UILabel alloc] initWithFrame:rt];
        [label setFont:t];
        [label setTextColor:[UIColor colorFromHex:@"#999999"]];
        [label setText:subName];
        [label setTextAlignment:NSTextAlignmentLeft];
        [cell.contentView addSubview:label];
        
        rt.origin.x =+ rt.size.width;
        rt.size.width = tableView.frame.size.width-20-rt.origin.x;
        rt.size.height = t.lineHeight;
        label = [[UILabel alloc] initWithFrame:rt];
        [label setFont:t];
        [label setTextColor:[UIColor colorFromHex:@"#999999"]];
        [label setText:[e stringForKey:@"fileSize"]];
        [label setTextAlignment:NSTextAlignmentRight];
        [cell.contentView addSubview:label];
        
//        if ([e boolForKey:@"isDir"])
//        {
//            rt = CGRectMake(tableView.width-34, (height-14)/2, 14, 14);
//            UIImageView *iconView = [[UIImageView alloc] initWithFrame:rt];
//            [iconView setImage:IMAGE_INIM_NAME(@"icon_arrow")];
//            [cell.contentView addSubview:iconView];
//        }
        rt = CGRectMake(25, height-1, tableView.frame.size.width-25, 1);
        UIImageView *iconView = [[UIImageView alloc] initWithFrame:rt];
        [iconView setBackgroundColor:RGB(245, 245, 245)];
        [cell.contentView addSubview:iconView];
        
        rt = CGRectMake(0, 0, tableView.frame.size.width, height);
        iconView = [[UIImageView alloc] initWithFrame:rt];
        [iconView setBackgroundColor:RGB(245, 245, 245)];
        [cell setSelectedBackgroundView:iconView];
    }
    [cell setSelectionStyle:UITableViewCellSelectionStyleNone];
    return cell;
}
        
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    [tableView deselectRowAtIndexPath:indexPath animated:YES];
    
    NSMutableDictionary *e = [dataList objectAtIndex:indexPath.row];
    self.ClickTreeBlock(e);
}
@end
 
@interface ZipRarViewController ()
@property (nonatomic,strong) UIBreadView *breview;
@end
 
@implementation ZipRarViewController
 
- (instancetype)init
{
    if (self = [super init])
    {
        
    }
    return self;
}
 
- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    self.breview = [UIBreadView breadViewWithFrame:CGRectMake(0, 0, ScreenWidth, self.safeAreaHeight) viewType:UIBreadViewBreadType];
    [self.view addSubview:self.breview];
    
    NSString *fileName = [self.filePath lastPathComponent]; //获取文件名称
    [self setTitle:[NSString stringWithFormat:@"%@",fileName]];
    if ([self.filePath.absoluteString hasPrefix:@"http"])
    {
        WeakSelf(self);
        [self showToastWithKeepAliveBlock:^(UIToastFenceView *waitView) {
            [waitView setViewMode:UIToastFenceViewModeCircle];
            [waitView setTitleText:@"下载中..."];
            
            NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
            
            AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:configuration];
            NSURLRequest *request = [NSURLRequest requestWithURL:self.filePath];
            
            NSURLSessionDownloadTask *downloadTask = [manager downloadTaskWithRequest:request progress:^(NSProgress *downloadProgress){
                
                CGFloat completedUnitCount = downloadProgress.completedUnitCount*1.0;
                CGFloat totalUnitCount = downloadProgress.totalUnitCount*1.0;
                CGFloat percent = completedUnitCount/totalUnitCount;
                
                [waitView setProgress:percent];
                if (percent == 1.0)
                {
                    [waitView setViewMode:UIToastFenceViewModeText];
                    [waitView setTitleText:@"下载完成"];
                    [waitView dismiss:1.5];
                }
            } destination:^NSURL *(NSURL *targetPath, NSURLResponse *response) {
                NSURL *url = [NSURL fileURLWithPath:userDocuments(@"docPathFile", fileName)];
                return url;
                
            } completionHandler:^(NSURLResponse *response, NSURL *filePath, NSError *error) {
                if (error) {
                    [waitView setViewMode:UIToastFenceViewModeText];
                    [waitView setTitleText:@"下载失败"];
                    [waitView dismiss:1.5];
                }
                else 
                {
                    [waitView dismiss];
                    NSURL *url = [NSURL fileURLWithPath:userDocuments(@"docPathFile", fileName)];
                    weakSelf.filePath = url;
                    [weakSelf toReadySource];
                }
            }];
            [downloadTask resume];
        }];
    }
    else
    {
        [self toReadySource];
    }
}
 
#pragma mark - 解压文件
- (void)toReadySource
{
    NSString *path = [self.filePath path];
    NSMutableArray *alist = [NSMutableArray arrayWithArray:[[self.filePath lastPathComponent] componentsSeparatedByString:@"."]];
    [alist removeLastObject];
    NSString *fileName = [alist componentsJoinedByString:@"."];
    NSString *to = userDocuments(@"zipsPathFile", @"");
    to = [to stringByAppendingPathComponent:[NSString stringWithFormat:@"%@_%@",self.fileFlag,fileName]];
    
    WeakSelf(self);
//    [[NSFileManager defaultManager] removeItemAtPath:to error:nil];
    if ([[NSFileManager defaultManager] fileExistsAtPath:to])
    {
        [self showToastBlock:^(UIToastFenceView *waitView) {
            
            [weakSelf readFiles:to fileName:fileName];
        }];
    }
    else
    {
        void (^doUnzipBlock)(void) = ^(void)
        {
            [weakSelf showToastBlock:^(UIToastFenceView *waitView)
            {
                [waitView setTitleText:@"解压中..."];
                BOOL flag =  [ZipRar7zUnArchive unarchiveFrom:path toPath:to password:nil];
//                BOOL flag = [SSZipArchive unzipFileAtPath:path toDestination:to];
                if (flag)
                {
                    [weakSelf readFiles:to fileName:fileName];
                }
                else
                {
                    [weakSelf showPlaceholderView];
                }
            }];
        };
//        if ([ZipRar7zUnArchive isPasswordProtectedAtPath:path])
//        {
//            NSLog(@"");
//        }
//        else
//        {
//            doUnzipBlock();
//        }
        doUnzipBlock();
    }
}
 
 
 
- (void)readFiles:(NSString *)path fileName:(NSString *)fileName
{
    NSMutableArray *list = [NSMutableArray arrayWithCapacity:0];
    NSFileManager *fm = [NSFileManager defaultManager];
    NSError *err = nil;
    NSArray *subFilePath = [fm contentsOfDirectoryAtPath:path error:&err];
    if (subFilePath)
    {
        for (NSString * fileName in subFilePath)
        {
            if (![fileName isEqualToString:@".DS_Store"])
            {
                NSMutableDictionary *e = [[NSMutableDictionary alloc] initWithCapacity:0];
                [e setObject:path forKey:@"path"];
                [e setObject:fileName forKey:@"fileName"];
                [e setObject:[fileName pathExtension] forKey:@"ext"];
                NSString *sub = [path stringByAppendingPathComponent:fileName];
                NSMutableDictionary *doj = [NSMutableDictionary dictionaryWithDictionary:[fm attributesOfItemAtPath:sub error:nil]];
                BOOL isDir = [[doj fileType] isEqualToString:NSFileTypeDirectory];
                [e setObject:boolToStr(isDir) forKey:@"isDir"];
                
                if (isDir)
                {
                    NSArray *rsub = [fm contentsOfDirectoryAtPath:[path stringByAppendingPathComponent:fileName] error:nil];
                    [e setObject:[NSString stringWithFormat:@"共%@个文件",intToStr(rsub.length)] forKey:@"fileCount"];
                    NSInteger s = CRMFileSizeByFilePath(sub);
                    [doj setObject:intToStr(s) forKey:NSFileSize];
                }
                else
                {
                    [e setObject:@"--" forKey:@"fileCount"];
                }
                
                CGFloat f = [doj fileSize] / 1000.0;
                NSString *fileSize = nil;
                if(f < 1000){
                    fileSize = [NSString stringWithFormat:@"%.1fKB",f];
                }
                else if (f < 1000.0 * 1000.0) {
                    f = round((f/1000.0) * 10) / 10.0;
                    fileSize = [NSString stringWithFormat:@"%.1fMB",f];
                }
                else{
                    f = round((f/1000.0/1000.0) * 10) / 10.0;
                    fileSize = [NSString stringWithFormat:@"%.1fGB",f];
                }
                
                [e setString:fileSize forKey:@"fileSize"];
                
                [list addObject:e];
            }
        }
        
        WeakSelf(self);
        dispatch_sync_on_main_queue(^{
            NSBreadData *d = [[NSBreadData alloc] init];
            [d setTitle:fileName];
            [self.breview addBreadViewTitle:d withConfigBlock:^UIView * _Nullable(NSInteger index, BOOL * _Nonnull useAnimation)
             {
                UINextTreeView *b = [[UINextTreeView alloc] init];
                [b setDataSource:list checkActionBlock:^(NSDictionary *e) {
                    NSString *p = [e stringForKey:@"path"];
                    NSString *n = [e stringForKey:@"fileName"];
                    if ([e boolForKey:@"isDir"])
                    {
                        p = [p stringByAppendingPathComponent:n];
                        [weakSelf readFiles:p fileName:n];
                    }
                    else
                    {
                        if (weakSelf.delegateBlock)
                        {
                            weakSelf.delegateBlock(e);
                        }
                    }
                }];
                return b;
            }];
        });
    }
    else
    {
        [self showPlaceholderView];
    }
}
 
 
 
- (void)showPlaceholderView
{
    WeakSelf(self);
    dispatch_sync_on_main_queue(^{
        [weakSelf.breview setHidden:YES];
    });
    [self showEmptyViewWithTitle:@"加载失败,请尝试刷新页面" icon:IMAGE_INIM_NAME(@"extractFail.png")];
    [self ajustEmptyViewWithIconSize:CGSizeMake(ScreenWidth*0.55, (ScreenWidth*0.55)/1.25)];
}
@end

面包屑实现(控制器内部实现切换子页面视图)

.h 头文件源码:

//
//  UIBreadView.h
//
//  Copyright © 2021 dtx. All rights reserved.
//
 
#import <UIKit/UIKit.h>
 
NS_ASSUME_NONNULL_BEGIN
 
@interface NSBreadData : NSObject
@property (nonatomic,strong) NSString *title;
@end
 
typedef NS_ENUM(NSInteger,UIBreadViewType){
    UIBreadViewBreadType=0, //面包屑模式,默认
    UIBreadViewSearchType,  //搜索模式
    UIBreadViewHyborType,   //混合模式(面包屑搜索均显示)
};
 
@interface UIBreadView : UIView
 
 
//仅有一个面包屑及动画切换的容器
+ (UIBreadView *)breadViewWithFrame:(CGRect)frame viewType:(UIBreadViewType)viewType;
 
//有一个面包屑及动画切换的容器及可添加的不进行动画切换的容器
+ (UIBreadView *)breadViewWithFrame:(CGRect)frame viewType:(UIBreadViewType)viewType positionFixView:(UIView * (^)(void))positionFixView;
 
- (void)addBreadViewTitle:(NSBreadData *)data withConfigBlock:(UIView* _Nullable (^)(NSInteger index,BOOL *useAnimation)) setupBlock;
 
//block返回YES,则隐藏面包屑
- (void)registerSelectActionBlock:(BOOL (^)(NSInteger index))actionBlock;
 
- (void)registerSearchKeywordBlock:(void (^)(NSString *keyword,BOOL willResign))aBlock;
 
//搜索UI自定义
- (void)registerConfigurationSearchInterfaceBlock:(void (^)(UIView *pview))aBlock;
 
- (BOOL)canPopToOneView;
- (CGFloat)breadNavigatorHeight;
- (CGFloat)breadNavigatory;
 
- (void)setHiddenNavigatorBreadHidden:(BOOL)flag;
 
- (UIView *)parentView:(UIView *)selfView;
- (UIView *)currentView;
 
 
- (void)resignSearchFieldResponder;
 
- (void)setReturnKeyType:(UIReturnKeyType)type;
//面包屑清除数据回归初始状态
- (void)breadcrumbClearingData;
@end
 
NS_ASSUME_NONNULL_END

.m 实现文件:

//
//  UIBreadView.m
//  Copyright © 2021 dtx. All rights reserved.
//
 
#import "UIBreadView.h"
#import "NSExtentionSloter.h"
#import "SearchBarDisplayCenter.h"
#import "imHeaders.h"
 
typedef BOOL (^UIBreadViewActionBlock)(NSInteger index);
 
typedef void (^UIBreadViewSearchKeywordBlock)(NSString *keyword,BOOL willResign);
 
@interface NSBreadData ()
@property (nonatomic,assign) BOOL isSelected;
 
@property (nonatomic,assign) CGFloat itemWidth;
 
//如果有sourceView,则内部处理点击事件切换页面,如果无,将事件反馈到控制器处理
@property (nonatomic,weak) UIView *sourceView;
@end
 
@implementation NSBreadData
@synthesize title,sourceView,itemWidth,isSelected;
@end
 
 
@interface UIBreadView ()<UICollectionViewDataSource,UICollectionViewDelegate,UICollectionViewDelegateFlowLayout,SearchBarDisplayCenterDelegate>
{
    UICollectionView *textView;
    UIView *contentView;
    
    NSInteger lastHiglightedRow;
    NSMutableArray *dataList;
}
 
@property (nonatomic,copy) UIBreadViewActionBlock actionBlock;
 
@property (nonatomic,copy) UIBreadViewSearchKeywordBlock searchBlock;
 
//是否显示搜索组件进行搜索,如果显示了搜索则不再显示面包屑
@property (nonatomic,assign) UIBreadViewType viewWorkType;
 
@property (nonatomic,strong) UIView *fixedContainerView;
 
@property (nonatomic, strong) UIImageView *sepView;
@end
 
@implementation UIBreadView
@synthesize actionBlock,viewWorkType;
 
+ (UIBreadView *)breadViewWithFrame:(CGRect)frame viewType:(UIBreadViewType)viewType
{
    UIBreadView *view = [[UIBreadView alloc] initWithFrame:frame];
    [view setViewWorkType:viewType];
    [view setupUI];
    return view;
}
 
 
+ (UIBreadView *)breadViewWithFrame:(CGRect)frame viewType:(UIBreadViewType)viewType positionFixView:(UIView * (^)(void))positionFixView
{
    UIBreadView *view = [[UIBreadView alloc] initWithFrame:frame];
    [view setViewWorkType:viewType];
    if (positionFixView)
    {
        UIView *fview = positionFixView();
        [view setFixedContainerView:fview];
    }
    [view setupUI];
    return view;
}
 
 
- (instancetype)initWithFrame:(CGRect)frame
{
    if (self = [super initWithFrame:frame])
    {
        self->lastHiglightedRow = -1;
        self.viewWorkType = UIBreadViewBreadType;
    }
    return self;
}
 
 
- (void)setFrame:(CGRect)frame
{
    [super setFrame:frame];
    CGRect rt = contentView.frame;
    rt.size.height = frame.size.height-rt.origin.y;
    [contentView setFrame:rt];
    
    for (UIView *aview in contentView.subviews)
    {
        [aview setFrame:CGRectMake(0, 0, frame.size.width, rt.size.height)];
    }
}
 
- (void)setupUI{
    dataList = [[NSMutableArray alloc] initWithCapacity:0];
    
    CGRect rt = CGRectMake(20, 0, self.bounds.size.width-40, 44);
    UICollectionViewFlowLayout *flowLayout=[[UICollectionViewFlowLayout alloc] init];
    [flowLayout setScrollDirection:UICollectionViewScrollDirectionHorizontal];
    
    textView = [[UICollectionView alloc] initWithFrame:rt collectionViewLayout:flowLayout];
//    textView.backgroundColor = [UIColor yellowColor];
    [textView setDelegate:self];
    [textView setDataSource:self];
//    [textView setPagingEnabled:YES];
    [textView setBackgroundColor:[UIColor clearColor]];
    [textView setShowsHorizontalScrollIndicator:NO];
    [textView setShowsVerticalScrollIndicator:NO];
    [textView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:@"dequeueIdentifier"];
    [self addSubview:textView];
    
    CGFloat y = rt.origin.y+rt.size.height;
    if (self.viewWorkType == UIBreadViewSearchType)
    {
        [textView setHidden:YES];
        CGRect rt = CGRectMake(0, 0, ScreenWidth, 50);
        UIView *header = [[UIView alloc]initWithFrame:rt];
        header.backgroundColor = [UIColor whiteColor];
 
        [header setTag:404];
        [self addSubview:header];
        
        SearchBarDisplayCenter *searchBar = [[SearchBarDisplayCenter alloc]initWithFrame:CGRectMake(0, 3, ScreenWidth, 47)];
        searchBar.userInteractionEnabled = YES;
        searchBar.placeholderStr = @"搜索";
        [searchBar breadChangeFrameAndSearchIcon];
 
//        searchBar.seachIconIV.image = [UIImage imageInImBundle:@"bread_search"];
//        
//        [searchBar.seachIconIV mas_updateConstraints:^(MASConstraintMaker *make) {
//          
//            make.width.mas_equalTo(17);
//            make.height.mas_equalTo(17);
//        }];
//
//        searchBar.placeholderColor = colorFromText(@"#999999");
        [searchBar setTag:405];
        searchBar.backgroundColor = [UIColor whiteColor];
        [searchBar setDelegate:self];
        [header addSubview:searchBar];
        
        y = rt.origin.y+rt.size.height;
    }
    else if (self.viewWorkType == UIBreadViewHyborType)
    {
        rt = CGRectMake(0, 0, ScreenWidth, 50);
        UIView *header = [[UIView alloc]initWithFrame:rt];
        header.backgroundColor = [UIColor whiteColor];
 
        [header setTag:404];
        [self addSubview:header];
        
        SearchBarDisplayCenter *searchBar = [[SearchBarDisplayCenter alloc]initWithFrame:CGRectMake(0, 3, ScreenWidth, 47)];
        [searchBar setTag:405];
        searchBar.userInteractionEnabled = YES;
        searchBar.placeholderStr = @"搜索";
 
        [searchBar breadChangeFrameAndSearchIcon];
 
 
        [searchBar setDelegate:self];
        [header addSubview:searchBar];
        
        y = rt.origin.y+rt.size.height;
        rt = textView.frame;
        rt.origin.y = y;
        [textView setFrame:rt];
        
        y = rt.origin.y+rt.size.height;
    }
    [textView setDataObject:NSStringFromCGRect(textView.frame)];
    
    rt = CGRectMake(0, y, self.bounds.size.width, 10);
    UIImageView *sepView = [[UIImageView alloc] initWithFrame:rt];
    self.sepView = sepView;
    [sepView setBackgroundColor:DSTextPlaceColor];
 
    [self addSubview:sepView];
    
    y = rt.origin.y+rt.size.height;
    if (self.fixedContainerView)
    {
        rt = self.fixedContainerView.frame;
        rt.origin.y = y;
        [self.fixedContainerView setFrame:rt];
        [self addSubview:self.fixedContainerView];
        y = rt.origin.y+rt.size.height;
    }
    rt = CGRectMake(0, y, self.bounds.size.width, self.bounds.size.height- (rt.origin.y+rt.size.height));
    contentView = [[UIView alloc] initWithFrame:rt];
    [contentView setBackgroundColor:[UIColor whiteColor]];
    [contentView setTag:90001];
    [self addSubview:contentView];
}
 
- (void)addBreadViewTitle:(NSBreadData *)data withConfigBlock:(UIView* _Nullable (^)(NSInteger index,BOOL *useAnimation)) setupBlock
{
    WeakSelf(self);
    __weak typeof(dataList) thisDataList = dataList;
    __weak typeof(textView) thisTextView = textView;
    __weak typeof(contentView) thisContentView = contentView;
    
    [textView performBatchUpdates:^{
        CGFloat w = [data.title stringSizeWithFont:[UIFont systemFontOfSize:14]].width;
        if (thisDataList.count > 0)
        {
            w += 28;
        }
        data.itemWidth = w;
        [data setIsSelected:NO];
 
        [thisDataList addObject:data];
        [thisTextView insertItemsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:thisDataList.count-1 inSection:0]]];
 
        if (thisDataList.count > 1)
        {
            NSBreadData *e = [thisDataList objectAtIndex:thisDataList.count-2];
            [e setIsSelected:YES];
 
            self->lastHiglightedRow = thisDataList.count-3;
            if (self->lastHiglightedRow > -1)
            {
//                e = [dataList objectAtIndex:lastHiglightedRow];
//                [e setIsSelected:NO];
//
//                [textView reloadItemsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:lastHiglightedRow inSection:0]]];
            }
 
            [thisTextView reloadItemsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:thisDataList.count-2 inSection:0]]];
        }
    } completion:^(BOOL finished) {
 
    }];
    if (setupBlock)
    {
        NSInteger count = contentView.subviews.count;
//        for (NSInteger idx=0;idx<contentView.subviews.count;idx++)
//        {
//            UIView *view = [contentView.subviews objectAtIndex:idx];
//            CGRect rt = view.frame;
//            rt.origin.x = - (count-idx)*self.bounds.size.width;
//            [view setFrame:rt];
//        }
        BOOL useAnimation = YES;
        UIView *view = setupBlock(count,&useAnimation);
        __weak typeof(view) thisView = view;
        data.sourceView = view;
        if (view)
        {
            CGRect rt = view.frame;
            rt.origin.x = thisContentView.subviews.count>0?weakSelf.bounds.size.width:0;
            rt.size = thisContentView.frame.size;
            [view setFrame:rt];
            [thisContentView addSubview:thisView];
 
            if (thisContentView.subviews.count>0)
            {
                if (useAnimation)
                {
                    [UIView animateWithDuration:0.3 animations:^
                    {
                        CGRect rt = thisView.frame;
                        rt.origin.x = 0;
                        [thisView setFrame:rt];
                    }
                    completion:^(BOOL finished)
                    {
 
                    }];
                }
                else
                {
                    CGRect rt = thisView.frame;
                    rt.origin.x = 0;
                    [thisView setFrame:rt];
                }
            }
        }
    }
    NSIndexPath *indexPath = [NSIndexPath indexPathForRow:self->dataList.count-1 inSection:0];
    [self->textView scrollToItemAtIndexPath:indexPath atScrollPosition:UICollectionViewScrollPositionRight animated:YES];
};
 
 
- (void)setReturnKeyType:(UIReturnKeyType)type
{
    SearchBarDisplayCenter *searchBar = [[self viewWithTag:404] viewWithTag:405];
    searchBar.searchField.returnKeyType = type;
}
 
- (UIView *)parentView:(UIView *)selfView
{
    NSInteger index = [contentView.subviews indexOfObject:selfView];
    if (index > 0)
    {
        return [contentView.subviews objectAtIndex:index-1];
    }
    return nil;
}
 
 
- (UIView *)currentView
{
    return [contentView.subviews lastObject];
}
 
 
 
- (void)registerSelectActionBlock:(BOOL (^)(NSInteger index))actionBlock
{
    [self setActionBlock:actionBlock];
}
 
 
- (void)registerSearchKeywordBlock:(void (^)(NSString *keyword,BOOL willResign))aBlock
{
    [self setSearchBlock:aBlock];
}
 
- (void)registerConfigurationSearchInterfaceBlock:(void (^)(UIView *pview))aBlock
{
    SearchBarDisplayCenter *searchBar = [[self viewWithTag:404] viewWithTag:405];
    CGRect ft = searchBar.frame;
    UIView *aview = [[UIView alloc] initWithFrame:ft];
    [aview setUserInteractionEnabled:YES];
    [[self viewWithTag:404] addSubview:aview];
    
    aBlock(aview);
}
 
- (CGSize)itemSizeWithIndex:(NSIndexPath *)indexPath{
    NSBreadData *data = [dataList objectAtIndex:indexPath.row];
    CGFloat w = data.itemWidth;
    return CGSizeMake(w+5, textView.frame.size.height);
}
 
 
- (void)setHiddenNavigatorBreadHidden:(BOOL)flag
{
    if (textView.isHidden != flag)
    {
        [textView setHidden:flag];
        if (flag)
        {
            [UIView animateWithDuration:0.3 animations:^
            {
                CGRect rt = self->textView.frame;
                rt.origin.y = -rt.size.height;
                [self->textView setFrame:rt];
                
                CGFloat y = 0;
                if ([self viewWithTag:404])
                {
                    y = [self viewWithTag:404].frame.origin.y+[self viewWithTag:404].frame.size.height;
                }
                
                rt = CGRectMake(0, y, self.bounds.size.width, 5);
                
                [self.sepView setFrame:rt];
                
                y += 5;
                
                rt = CGRectMake(0, y, self.bounds.size.width, self.bounds.size.height-y);
                [self->contentView setFrame:rt];
            }
            completion:^(BOOL finished)
            {
                CGRect rt = self->textView.frame;
                rt.origin.x = self.bounds.size.width;
                rt.origin.y = 0;
                [self->textView setFrame:rt];
            }];
        }
        else
        {
            [UIView animateWithDuration:0.3 animations:^
            {
                CGRect rt = self->textView.frame;
                rt.origin.x = 20;
                rt.origin.y = CGRectFromString((NSString *)[self->textView dataObject]).origin.y;
                [self->textView setFrame:rt];
                
                CGFloat y = rt.origin.y+rt.size.height;
                
                rt = CGRectMake(0, y, self.bounds.size.width, 5);
                
                [self.sepView setFrame:rt];
                
                y += 5;
                
                rt = CGRectMake(0, y, self.bounds.size.width, self.bounds.size.height-y);
                [self->contentView setFrame:rt];
            }
            completion:^(BOOL finished)
            {
                
            }];
        }
    }
    for (UIView *aview in contentView.subviews)
    {
        [aview setFrame:CGRectMake(0, 0, contentView.size.width, contentView.size.height)];
    }
}
 
 
- (BOOL)canPopToOneView
{
    BOOL flag = NO;
    if (dataList.count==0) {
        return flag;
    }
    NSBreadData *mObject = [dataList lastObject];
    if (mObject.sourceView && dataList.count>1)
    {
        flag = YES;
        [self poptoOneViewWithAnimation:dataList.count-2];
    }
    
    return flag;
}
 
 
- (CGFloat)breadNavigatorHeight
{
    if (self.viewWorkType == UIBreadViewSearchType)
    {
        return MAX(textView.frame.size.height, [self viewWithTag:404].frame.size.height);
    }
    else if (self.viewWorkType == UIBreadViewHyborType)
    {
        return textView.frame.size.height+[self viewWithTag:404].frame.size.height;
    }
    else
    {
        return textView.frame.size.height;
    }
}
 
 
- (CGFloat)breadNavigatory
{
    return [self viewWithTag:404].frame.origin.y+[self viewWithTag:404].frame.size.height;
}
 
 
- (void)resignSearchFieldResponder
{
    SearchBarDisplayCenter *searchBar = [[self viewWithTag:404] viewWithTag:405];
    [searchBar restoreInitialState];
    [searchBar.searchField resignFirstResponder];
    [self poptoOneViewWithAnimation:1];
}
 
 
- (void)poptoOneViewWithAnimation:(NSInteger)row
{
    if (self.actionBlock)
    {
        BOOL flag =  self.actionBlock(row);
        [self setHiddenNavigatorBreadHidden:flag];
    }
    
    NSBreadData *mObject = [dataList objectAtIndex:row];
    if (mObject.sourceView)
    {
        for (NSInteger ix=dataList.count-1; ix>row;ix--)
        {
            NSBreadData *mObject = [dataList objectAtIndex:ix];
            UIView *aview = mObject.sourceView;
            [UIView animateWithDuration:0.3 animations:^
            {
                CGRect rt = aview.frame;
                rt.origin.x = self.bounds.size.width;
                [aview setFrame:rt];
            }
            completion:^(BOOL finished)
            {
                [aview removeFromSuperview];
            }];
        }
        [dataList removeObjectsInRange:NSMakeRange(row+1, dataList.count- (row+1))];
        NSBreadData *m = [dataList lastObject];
        [m setIsSelected:NO];
        
        if (dataList.count > 1)
        {
            m = [dataList objectAtIndex:dataList.count-2];
            [m setIsSelected:YES];
        }
        
        if (textView)
        {
            [textView reloadSections:[NSIndexSet indexSetWithIndex:0]];
        }
    }
   
}
 
 
- (void)breadcrumbClearingData {
    if (self.actionBlock)
    {
        BOOL flag =  self.actionBlock(0);
        [self setHiddenNavigatorBreadHidden:flag];
    }
    if (dataList.count > 0) {
//        for (NSInteger ix=0; ix>dataList.count;ix++)
//        {
//            NSBreadData *mObject = [dataList objectAtIndex:ix];
//            UIView *aview = mObject.sourceView;
//                [aview removeFromSuperview];
//        }
        [dataList removeAllObjects];
    }
    if (textView)
    {
        [textView reloadSections:[NSIndexSet indexSetWithIndex:0]];
    }
}
#pragma mark - SearchBarDisplayCenterDelegate
- (void)getSearchKeyWordWhenChanged:(NSString *)searchWord
{
    if (self.searchBlock)
    {
        self.searchBlock(searchWord,NO);
    }
}
 
 
- (void)textFieldBecomeResponder:(NSString*)text
{
    if (self.searchBlock)
    {
        self.searchBlock(text,NO);
    }
}
 
- (void)textFieldResignResponder:(NSString*)text
{
    if (self.searchBlock)
    {
        self.searchBlock(text,YES);
    }
}
 
#pragma mark - UICollectionView
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
{
    return 1;
}
 
 
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
    NSInteger count = dataList.count;
    return count;
}
 
 
 
- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumLineSpacingForSectionAtIndex:(NSInteger)section
{
    return 0;
}
 
 
- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section
{
    return 0;
}
 
 
 
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
{
    return [self itemSizeWithIndex:indexPath];
}
 
 
 
// The cell that is returned must be retrieved from a call to -dequeueReusableCellWithReuseIdentifier:forIndexPath:
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
    UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"dequeueIdentifier" forIndexPath:indexPath];
    [cell.contentView setBackgroundColor:[UIColor clearColor]];
 
    CGSize size = [self itemSizeWithIndex:indexPath];
    NSBreadData *mObject = [dataList objectAtIndex:indexPath.row];
    CGFloat x = 5;
    if (indexPath.row>0)
    {
        CGRect rt = CGRectMake(x, (size.height-10)/2, 10, 10);
        UIImageView *iconView = [cell.contentView viewWithTag:90];
        if (!iconView)
        {
            iconView = [[UIImageView alloc] initWithFrame:rt];
            [iconView setImage:IMAGE_INIM_NAME(@"bookList_xiayiye")];
            [iconView setTag:90];
            [cell.contentView addSubview:iconView];
        }
        if (mObject.isSelected)
        {
            [iconView setTintColor:RGB(0, 125, 255)];
        }
        
        x += rt.size.width;
    }
    
    [[cell.contentView viewWithTag:90] setHidden:indexPath.row == 0];
    
    CGRect rt = CGRectMake(x, 0, size.width-x, size.height);
    UIButton *button = [cell.contentView viewWithTag:91];
    if (!button)
    {
        button = [UIButton buttonWithType:UIButtonTypeCustom];
        [button setTag:91];
        [button.titleLabel setFont:[UIFont systemFontOfSize:14]];
        [button setTitleColor:RGB(40, 40, 40) forState:UIControlStateNormal];
        [button setTitleColor:RGB(0, 125, 255) forState:UIControlStateSelected];
        [cell.contentView addSubview:button];
    }
    [button setUserInteractionEnabled:NO];
    [button setFrame:rt];
    [button setTitle:mObject.title forState:UIControlStateNormal];
    [button setDataObject:mObject];
    [button setSelected:mObject.isSelected];
    
    [cell setBackgroundColor:[UIColor clearColor]];
    
    return cell;
}
 
 
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
//    UICollectionViewCell *cell = [contentView cellForItemAtIndexPath:indexPath];
    [self poptoOneViewWithAnimation:indexPath.row];
}
@end

使用

ZipRarViewController *c = [[ZipRarViewController alloc] init];
//传入压缩包路径,支持本地文件及远程文件URL
[c setFilePath:url];
//文中路径进行MD5,目的是区分记录解压记录,已解压过的后期不再解压而是直接读取展示
[c setFileFlag:[fileFlag md5Byte32Lower]];
[c setDelegateBlock:^(NSDictionary *data) {
    NSString *p = [data stringForKey:@"path"];
    NSString *f = [data stringForKey:@"fileName"];
    weakSelf.fileURL = [NSURL fileURLWithPath:[p                 
       stringByAppendingPathComponent:f]];
    [weakSelf openDocument:weakSelf.fileURL];
}];
[self.navigationController pushViewController:c animated:YES];
 
 
//打开文件方法
- (void)openDocument:(NSURL *)url {
    if ([QLPreviewController canPreviewItem:url]) {
            dispatch_async(dispatch_get_main_queue(), ^{
                [weakSelf initKkpreview];
            });
        }
}
 
 
- (void)initKkpreview {
    self.kkpreview = [[QLPreviewController alloc] init];
    self.kkpreview.delegate = self;
    self.kkpreview.dataSource = self;
    WeakSelf(self);
    UIView *rview = [UINaviButton naviButtonWithActionBlock:^{
        [weakSelf.kkpreview.navigationController popViewControllerAnimated:YES];
    }];
    UIBarButtonItem *item = [[UIBarButtonItem alloc]initWithCustomView:rview];
    self.kkpreview.navigationItem.leftBarButtonItem = item;
    [self.navigationController pushViewController:self.kkpreview animated:YES];
}
 
 
#pragma mark - QLPreviewControllerDataSource
- (NSInteger)numberOfPreviewItemsInPreviewController:(QLPreviewController *)controller{
    return 1;
}
 
- (id<QLPreviewItem>)previewController:(QLPreviewController *)controller previewItemAtIndex:(NSInteger)index{
    return  self.fileURL;
}