技术点两点: tableview 封装 与 webview 缓存实现
-
刷新tableview封装:MBARefreshTableView(集成mjRefresh与DZNEmpty空视图)。
由于项目出现了tableview重复出现数据或tableview数组越界的bug。所以重新整理思路,对刷新talbeView封装。
10月技术点记录3中,虽然用如下暂时阻止了越界问题。但是 这方案是不可取的,有逻辑错误才会导致此问题。
//即:
if(self.dataArray.count != 0 && indexPath.row < self.dataArray.count){
Model * model = self.dataArray[indexPath.row];
}
解决如上问题:
主要是上下刷新没有控制好。就是只能存在一个网络数据去请求。(1.多bloading判断,直到请求结束才能bloading改false。2.然后如果在上拉刷新,就控制不让下拉刷新。如果是在下拉刷新,就不让上拉刷新。 即只存在一个刷新在处理)
所以从新整理MBARefreshTableView逻辑如下(如有好的建议,欢迎一起探讨):
#import <UIKit/UIKit.h>
@interface MBARefreshTableView : UITableView<MBASkinProtocol, UITableViewDelegate, UITableViewDataSource,DZNEmptyDataSetSource,DZNEmptyDataSetDelegate>
-(void)initUI;
@property (strong, nonatomic) NSMutableArray *mDataItems;
@property (assign, nonatomic) BOOL bHeader;//是否头部请求
@property (assign, nonatomic) BOOL bRefresh;//是否可刷新
@property (assign, nonatomic) BOOL bOnlyHeaderRefresh;
@property (assign, nonatomic) BOOL bOnlyFooterRefresh;
@property (copy, nonatomic) void(^didSelectedHandler)(NSIndexPath *indexPath);
// refresh handler
@property (copy, nonatomic) void(^headerRefreshHandler)(void);
@property (copy, nonatomic) void(^footerRefreshHandler)(void);
// header
- (void)startHeaderRefresh;
- (void)stopHeaderRefresh;
// footer
- (void)startFooterRefresh;
- (void)stopFooterRefresh;
-(void)stopRefresh;
@end
#import "MBARefreshTableView.h"
@implementation MBARefreshTableView
-(instancetype)init
{
if(self = [super init])
{
[self _init];
}
return self;
}
-(instancetype)initWithFrame:(CGRect)frame {
if(self = [super initWithFrame:frame])
{
[self _init];
}
return self;
}
-(instancetype)initWithFrame:(CGRect)frame style:(UITableViewStyle)style {
if(self = [super initWithFrame:frame style:style])
{
[self _init];
}
return self;
}
-(void)_init{
self.delegate = self;
self.dataSource = self;
self.emptyDataSetSource = self;
self.emptyDataSetDelegate = self;
self.separatorInset = UIEdgeInsetsZero;
if([self respondsToSelector:@selector(cellLayoutMarginsFollowReadableWidth)])
{
self.cellLayoutMarginsFollowReadableWidth = false;
}
//刷新操作只能一个
MBAWeakSelf
MJRefreshNormalHeader *header = [MJRefreshNormalHeader headerWithRefreshingBlock:^{
if([weakSelf.mj_footer isRefreshing]){
[weakSelf.mj_header endRefreshing];
return;
}
[weakSelf handlerHeaderRefresh];
if(weakSelf.headerRefreshHandler) weakSelf.headerRefreshHandler();
}];
self.mj_footer = [MJRefreshAutoNormalFooter footerWithRefreshingBlock:^{
if ([weakSelf.mj_header isRefreshing]) {
[weakSelf.mj_footer endRefreshing];
return;
}
[weakSelf handlerFooterRefresh];
if(weakSelf.footerRefreshHandler) weakSelf.footerRefreshHandler();
}];
header.lastUpdatedTimeLabel.hidden = true;
self.mj_header = header;
[self.mj_footer setHidden:true];
self.tableHeaderView = [UIView new];
self.tableFooterView = [UIView new];
self.backgroundColor = [UIColor clearColor];
self.mDataItems = [NSMutableArray array];
self.bHeader = false;
self.bRefresh = true;
[self initUI];
self.mPageNum = 0;
self.estimatedRowHeight = 0;
self.estimatedSectionHeaderHeight = 0;
self.estimatedSectionFooterHeight = 0;
}
-(void)initUI{
}
-(void)setItems:(NSArray *)array alwaysHeaderRefresh:(BOOL)alwaysHeaderRefresh{
if (self.bHeader && array) {
if (!alwaysHeaderRefresh) {
self.bHeader = NO;
}
if (self.bOnlyHeaderRefresh || !self.bRefresh) {
[self.mj_footer setHidden:true];
}else{
[self.mj_footer setHidden:(array.count == 0)];
}
[self.mDataItems removeAllObjects];
}
[self.mDataItems addObjectsFromArray:array];
if (array.count == 0 && !self.bHeader) { // 不是头部的时候,获取数据为空,底部重设置没有更多数据
[self.mj_footer endRefreshingWithNoMoreData];
}else{
[self stopFooterRefresh];
}
[self requestFinish:!alwaysHeaderRefresh];
}
- (void)requestFinish:(BOOL)bFinish
{
if(bFinish) {
[self stopHeaderRefresh];
[self reloadData];
}
}
#pragma mark - tableviewDelegate
- (NSInteger)numberOfRowsInSection:(NSInteger)section
{
return 0;
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView deselectRowAtIndexPath:indexPath animated:YES];
if(self.didSelectedHandler) self.didSelectedHandler(indexPath);
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
return nil;
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return self.mDataItems.count;
}
#pragma mark - emptyDelegate
-(UIImage *)imageForEmptyDataSet:(UIScrollView *)scrollView{
if ([MBANetCheck checkNetAvailable]) {
return [UIImage imageNamed:@"MBALibFramework.bundle/mba_empty"];
}else{
return [UIImage imageNamed:MBAReadImageName(@"WFReloadView.bundle/img_error_reloadView")];
}
}
-(NSAttributedString *)titleForEmptyDataSet:(UIScrollView *)scrollView{
if ([MBANetCheck checkNetAvailable]) {
return [@"数据为空" yb_makeAttributes:^(YBAttributeMake *make) {
make.yb_font([UIFont systemFontOfSize:MBAReadFloat(UI_COMPONENT_FONT_DEFAULT)]).yb_all();
}];
}else{
return [@"网络异常,请查看网络设置" yb_makeAttributes:^(YBAttributeMake *make) {
make.yb_font([UIFont systemFontOfSize:MBAReadFloat(UI_COMPONENT_FONT_DEFAULT)]).yb_all();
}];
}
}
-(BOOL)emptyDataSetShouldAllowScroll:(UIScrollView *)scrollView{
return true;
}
- (void)emptyDataSetWillAppear:(UIScrollView *)scrollView {
// 如果你的空白占位图与需求向左,发生偏移,可如下设置:
self.contentOffset = CGPointZero;
}
- (void)updateUI
{
}
- (void)updateColor
{
self.separatorColor = MBAReadColor(UI_COMPONENT_LINE_COLOR);
}
- (void)updateLanguage
{
}
-(void)handlerHeaderRefresh{
self.bHeader = true;
[self.mDataItems removeAllObjects];
self.mPageNum = 0;
}
-(void)handlerFooterRefresh{
self.bHeader = false;
self.mPageNum ++;
}
// header
- (void)startHeaderRefresh{
[self.mj_header beginRefreshing];
}
- (void)stopHeaderRefresh{
[self.mj_header endRefreshing];
}
// footer
- (void)startFooterRefresh{
[self.mj_footer beginRefreshing];
}
- (void)stopFooterRefresh{
[self.mj_footer endRefreshing];
}
-(void)stopRefresh{
[self stopHeaderRefresh];
[self stopFooterRefresh];
}
-(void)setBRefresh:(BOOL)bRefresh{
_bRefresh = bRefresh;
[self.mj_header setHidden:!bRefresh];
[self.mj_footer setHidden:!bRefresh];
}
-(void)setBOnlyHeaderRefresh:(BOOL)bOnlyHeaderRefresh{
_bOnlyHeaderRefresh = bOnlyHeaderRefresh;
[self.mj_header setHidden:!bOnlyHeaderRefresh];
[self.mj_footer setHidden:bOnlyHeaderRefresh];
}
-(void)setBOnlyFooterRefresh:(BOOL)bOnlyFooterRefresh{
_bOnlyFooterRefresh = bOnlyFooterRefresh;
[self.mj_header setHidden:bOnlyFooterRefresh];
[self.mj_footer setHidden:!bOnlyFooterRefresh];
}
@end
-
webView网页图片离线缓存实现
很多时候我们需要webView离线缓存一些数据,如图片等.
这时就需要NSURLProtocol来实现这功能.
虽然它是一个协议,但是它其实是一个类,而且继承自NSObject。它的作用是处理特定URL协议的加载。它本身是一个抽象类,提供了使用特性URL方案处理URL的基础结构。你可以自己创建NSURLProtocol的子类,来让自己的应用支持自定义的协议或者URL方案。
应用程序永远不需要直接实例化一个NSURLProtocol子类。当一个下载开始的时候,系统创建一个合适的protoco对象来响应URL请求。你要做的就是自己定义一个你自己的protocol,然后在APP启动的时候调用registerClass:,让系统知道你的协议。
UIWebView的离线缓存处理
首先,我们需要自定义一个NSURLProtocol的子类,并且在AppDelegate.m的
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[NSURLProtocol registerClass:[ZGURLProtocol MBAWebviewImageProtocol]];
return YES;
}
当URL Load System开始加载一个请求的时候,每个注册的协议类都被依次去调用,以确定是否可以用指定的请求去初始化它。首先被调用的方法是:
+ (BOOL)canInitWithRequest:(NSURLRequest *)request;
在该方法里面进行缓存过滤,如缓存图片,就需要判断路径是否是图片,如果是图片就返回true.
if ([self isImageRequest:path]){
return ture;
}else{
return false;
}
如果返回ture,那么就相当于该请求被自定义的URLProtocol来处理。
然后我们在startLoading方法进行缓存处理操作,demo里面有[SDWebImageDownloader sharedDownloader]进行了下载缓存.然后一些业务处理
最后就是一些代码方法:(直接回调即可)
#pragma mark - NSURLConnectionDelegate
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
[self.client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[self.client URLProtocol:self didLoadData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
[self.client URLProtocolDidFinishLoading:self];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
[self.client URLProtocol:self didFailWithError:error];
}
MBAWebviewImageProtocol文件
MBAWebviewImageProtocol.h
#import <Foundation/Foundation.h>
@interface MBAWebviewImageProtocol : NSURLProtocol
@end
MBAWebviewImageProtocol.m
static NSString *const WebviewImageProtocolHandledKey = @"WebviewImageProtocolHandledKey";
@interface MBAWebviewImageProtocol () <NSURLConnectionDataDelegate>
@property(nonatomic, strong) NSURLConnection *connection;
@end
@implementation MBAWebviewImageProtocol
+ (BOOL)canInitWithRequest:(NSURLRequest *)request {
NSString *path = request.URL.path;
if ([self isImageRequest:path]) {
if ([NSURLProtocol propertyForKey:WebviewImageProtocolHandledKey inRequest:request]) {
return NO;
}
return YES;
}
return NO;
}
+ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request {
return request;
}
- (void)startLoading {
NSString *urlString = self.request.URL.absoluteString;
NSRange range = [urlString rangeOfString:@"?"];
if(range.location!=NSNotFound){
urlString = [urlString substringToIndex:range.location];
[self handlerCacheImage:urlString];
return;
}
if ([MBAImageLoadManager readImageLoad] == ImageLoadNO || ([MBAImageLoadManager readImageLoad] == ImageLoadOnlyWifi && [MBANetCheck isEnable3G])) {//.不加载处理
NSString *pathResource = nil;
if ([self isTitleIconImg:urlString]) {
if ([MBAThemeManager readThemeStyle] == ThemeStyleDay) {
pathResource = @"mba-module-detailsweb.bundle/btn_show_contents_normal.png";
}else{
pathResource = @"mba-module-detailsweb.bundle/btn_show_contents_pressed.png";
}
NSData *imgUnloadData = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:pathResource ofType:nil]];
[self myResponseImageData:imgUnloadData];
}else{
NSData *imageData = (NSData *)[[CacheManager manager]objectForKey:urlString group:@"image"] ;
if (imageData) {
[self myResponseImageData:imageData];
}else{
pathResource = @"mba-module-common.bundle/img_unload_replace.png";
NSData *imgUnloadData = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:pathResource ofType:nil]];
[self myResponseImageData:imgUnloadData];
}
}
} else{
[self handlerCacheImage:urlString];
}
}
-(void)handlerCacheImage:(NSString *)urlString{
NSData *imageData = (NSData *)[[CacheManager manager]objectForKey:urlString group:@"image"] ;
if (imageData) {
[self myResponseImageData:imageData];
} else {
[[SDWebImageDownloader sharedDownloader]downloadImageWithURL:self.request.URL options:SDWebImageDownloaderUseNSURLCache progress:nil completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, BOOL finished) {
[self myResponseImageData:data];
[[CacheManager manager]setObject:data forKey:urlString group:@"image"];
}];
}
}
-(void)myResponseImageData:(NSData *)data{
NSString *mimeType = @"image/jpeg";
NSMutableDictionary *header = [[NSMutableDictionary alloc] initWithCapacity:2];
NSString *contentType = [mimeType stringByAppendingString:@";charset=UTF-8"];
header[@"Content-Type"] = contentType;
header[@"Content-Length"] = [NSString stringWithFormat:@"%lu", (unsigned long) data.length];
NSHTTPURLResponse *httpResponse = [[NSHTTPURLResponse alloc] initWithURL:self.request.URL
statusCode:200
HTTPVersion:@"1.1"
headerFields:header];
[self.client URLProtocol:self didReceiveResponse:httpResponse cacheStoragePolicy:NSURLCacheStorageNotAllowed];
[self.client URLProtocol:self didLoadData:data];
[self.client URLProtocolDidFinishLoading:self];
}
- (void)stopLoading {
[self.connection cancel];
}
//private
+ (BOOL)isImageRequest:(NSString *)URLString
{
return [UrlUtils isImage:URLString];
}
- (BOOL)isTitleIconImg:(NSString *) URLString {
NSRange columnImgRange = [URLString rangeOfString:@"columntitel-ico.png"];
NSRange ngColumnImgRange = [URLString rangeOfString:@"columntitel-ng-ico.png"];
if(columnImgRange.length > 0 || ngColumnImgRange.length > 0) {
return YES;
}
return NO;
}
#pragma mark - NSURLConnectionDelegate
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
[self.client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[self.client URLProtocol:self didLoadData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
[self.client URLProtocolDidFinishLoading:self];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
[self.client URLProtocol:self didFailWithError:error];
}
网友评论