一.引言
从今天开始,在我的博客上开辟工作项目专栏,来总结记录项目中的开发难点。第一篇记录的是电子书模块。
二.项目需求
一级界面:1.侧滑菜单、2.书籍列表、3.上拉加载
二级界面:1.头部书籍信息、2.书籍简介、3.评价列表、4.评价功能(弹出评价界面)5.底部下载/阅读功能
下载管理界面:1.下载的书籍信息、2.可侧滑删除
阅读界面:1.电子书自适应大小(pdf文件)、2.翻页 、3.记录页
三.总结(后台拿到的测试数据)
1.一级界面
0.0
1.pid为1的为父类,sn进行排序的标志,pid不为1的为子类
*遇到难点:把对应的子类按顺序放到对应的父类下
*解决方法:
NSMutableArray *allArrM = [NSMutableArray array];
NSMutableArray *childArrM = [NSMutableArray array];
for (BookMenuEntity *menu in result) {
/**
* pid为1的是父类
*/
if ([menu.pid isEqual:@1]) {
[allArrM addObject:menu];
}
else
[childArrM addObject:menu];
}
/**
*所有父类+排序
*/
NSSortDescriptor *allSn = [[NSSortDescriptor alloc] initWithKey:@"sn" ascending:YES];
NSArray *newAllResult= [allArrM sortedArrayUsingDescriptors:@[allSn]];
/**
*所有子类+排序
*/
NSSortDescriptor *childSn = [[NSSortDescriptor alloc] initWithKey:@"sn" ascending:YES];
NSArray *childResult= [childArrM sortedArrayUsingDescriptors:@[childSn]];
/*
对应父类有几个,这个可变数组救添加几个数组
*/
NSMutableArray *newChildResult = [NSMutableArray array];
for (int i =0; i<newAllResult.count; i++) {
NSMutableArray *tempArrM = [[NSMutableArray alloc] init];
[newChildResult addObject:tempArrM];
}
int i = 0;
for (BookMenuEntity *allEntity in newAllResult) {
/*
子类pid找到对应父类id,对应上就加到对应的数组(相当于加到对应的父类)
*/
for (BookMenuEntity *childEntity in childResult) {
if ([childEntity.pid isEqual:allEntity.ID]) {
[newChildResult[i] addObject:childEntity];
}
}
i++;
}
self.allMenuArrM = (NSMutableArray *)newAllResult;
self.childMenuArrM = newChildResult;
[self loadMenuData];
1.1
1.1.1
因为要让菜单栏弹出时书籍列表(UICollectionViewController)不可操作,所以加了背景遮罩。
backView = [[UIView alloc] initWithFrame:CGRectMake(0, HEADER, self.view.frame.size.width, self.view.frame.size.height)];
backView.backgroundColor = [UIColor colorWithRed:0 green:0 blue:0 alpha:0.3];
//加到标签栏上
[self.tabBarController.view addSubview:backView];
//单击手势,让菜单收起
tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapGestureAction:)];
[tapGesture setNumberOfTapsRequired:1];
[backView addGestureRecognizer:tapGesture];
//为了让菜单的透明度和背景区分,再加个小的背景
view = [[UIView alloc] initWithFrame:CGRectMake(0, HEADER, self.view.frame.size.width/2.5, backView.frame.size.height)];
view.backgroundColor = [UIColor blackColor];
view.alpha = 0.7;
[view addSubview:self.menuTableView];
[self.tabBarController.view addSubview:view];
//没有点击菜单按钮,默认为隐藏
backView.hidden = YES;
view.hidden = YES;
1.1.2
*遇到难点:(1)刚开始时,菜单能正常加到tabBarController.view上,后来push到二级界面后,就不能加到tabBarController.view上(presentd的可以)。
*解决方法:[self.tabBarController.view bringSubviewToFront:backView];
[self.tabBarController.view bringSubviewToFront:view];
1.1.3
菜单点击需求。1??点到谁,谁就变红(表示选中,父类也可以被选中并进行数据加载)2??选中以后,再点击,不进行数据请求3??点击一个父类,其它父类子菜单要全部收起
思路:用一个数组来记录点击选中状态、以及展开闭合状态
/**
* 1展开,0收起
*/
- (void)loadMenuData {
//用0代表收起,非0(不一定是1)代表展开,默认都是收起的
for (int i = 0; i < self.allMenuArrM.count; i++) {
[self.isExpland addObject:@0];
}
if (self.isExpland.count != 0) {
[self.menuTableView reloadData];
}
}
#pragma mark 加载菜单栏父类是否被选中的数据
/**
* 默认第一个父类被选中,1被选中,0没有
*/
- (void)parentChooseAction{
for (int j=0; j<self.allMenuArrM.count;j++) {
if (j==0) {
self.isParentChooseArrM[j] = @1;
}
else{
self.isParentChooseArrM[j] = @0;
}
}
}
#pragma mark 加载菜单栏子类是否被选中的数据
/**
* 1被选中,0没有
*/
- (void)childChoosAction{
for (int i=0; i<self.childMenuArrM.count; i++) {
NSMutableArray *arrM = [NSMutableArray array];
NSMutableArray *arrM2 = self.childMenuArrM[i];
for (int j=0; j<arrM2.count;j++ ) {
[arrM addObject:@0];
}
[self.childIsChooseArrM addObject:arrM];
}
}
1.1.4
*遇到难点:(4)因为菜单涉及到很多数据,然后进行多次表格刷新。所以容易出现数组越界
*解决方法:数组为空的时候可能进行了reloadTableView,进行判空操作。尽量减掉重复的reloadTabelView(一开始有四处,后来减到两处)
1.2
1.2.1
*遇到难点:(1)布局collectionViewCell
*解决方法:其它都好弄,但在解决每个cell的间距的时候一定要记住一个属性:minimumInteritemSpacing,不设置它,cell的间距调不了。记得代理UICollectionViewDelegateFlowLayout。
UICollectionViewFlowLayout *flowLayout = [[UICollectionViewFlowLayout alloc] init];
[flowLayout setScrollDirection:UICollectionViewScrollDirectionVertical];
self.collectionView.collectionViewLayout = flowLayout;
flowLayout.minimumInteritemSpacing = 12.5;
[self.collectionView registerClass:[EBookCollectionViewCell class] forCellWithReuseIdentifier:reuseIdentifier];
/*
设置cell的大小
*/
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
return CGSizeMake(90, 202);
}
/*
cell的布局上左下右的屏幕间距
*/
- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout insetForSectionAtIndex:(NSInteger)section {
return UIEdgeInsetsMake(10, 12.5, 10,12.5);
}
1.2.2
*遇到难点:(2)书籍介绍需求默认可以显示三行,但是字不够的时候,如何让字顶住左上角
*解决方法:
_introduceLabel.lineBreakMode = NSLineBreakByWordWrapping;//一定要设置这个属性
cell.introduceLabel.text = [NSString stringWithFormat:@"%@\n\n",@"哈哈哈"];
1.2.3
*遇到难点:(3)字体天空蓝
*解决方法:推荐一个好用的第三方颜色器(都调好了)http://download.csdn.net/detail/xj_love/9542251
_introduceLabel.textColor = [UIColor colorWithRed:0/255.0f green:178/255.0f blue:238/255.0f alpha:1.0];
2.二级界面
2.1
2.1.1
!
//注意书籍名标签字的位置1.2.2的难点(2)一样
2.1.2
!
公司用autoLayout进行布局。
*遇到难点:(1)评价内容Label高度自适应(2)cell高度自适应
*解决方法:1??获取文字高度:
/*.h文件*/
@property (strong, nonatomic) IBOutlet NSLayoutConstraint *reviewLabelHeight;
/*.m文件*/
- (void)layoutSubviews{
CGSize size = [_review_content.text boundingRectWithSize:CGSizeMake(300, 1000) options: NSStringDrawingTruncatesLastVisibleLine|NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName:[UIFont systemFontOfSize:13.0]} context:nil].size;
_reviewLabelHeight.constant = size.height;
}
2.2
2.2.1
同样的,弄个背景View加到self.view上。添加单击手势。
2.2.2
*遇到难点:(1)在textView上加一个类似placheholder的提示语。(textfile的有这个属性,textView没有)
*解决方法:在textview上加一个Label,然后在textView的代理方法中 #pragma mark textView代理方法
- (void)textViewDidBeginEditing:(UITextView *)textView {
self.promptLabel.hidden = YES;
2.2.3
*遇到难点:(2)怎样判断textView没有输入内容已经输入的为空格。
*解决方法:
/*
//进行一个过滤,找出空格
NSCharacterSet *set = [NSCharacterSet whitespaceAndNewlineCharacterSet];
NSString *trimedString = [self.textView.text stringByTrimmingCharactersInSet:set];
*/
if (self.textView.text.length !=0&&trimedString.length !=0) {
}
3.下载管理界面
3.1
3.1.1
*遇到难点:书名高度自适应,cell高度自适应
*解决方法:参考2.1.2
3.1.2
实现侧滑删除:
/**
* 1.开启允许编辑
* 2.提交编辑
* 3.把英文转换成中文
*/
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath{
return YES;
}
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath{
if (editingStyle == UITableViewCellEditingStyleDelete) {
DownBookInfoEntity *listModel = self.downloadArrM[indexPath.row];
[DAO_DownBook deleteBookInfoWithBookID:(NSString *)listModel.ID];
NSString *bookUrl = [NSString stringWithFormat:@"%@%@",Host1,listModel.file];
NSString *path = [MANAGER_FILE.CSDownloadPath stringByAppendingPathComponent:[NSString stringWithFormat:@"file/%@", [bookUrl lastPathComponent]]];
/*从数据库删除*/
[MANAGER_FILE deleteFolderPath:path];
[self.downloadrrM removeObjectAtIndex:indexPath.row];
//数组是[indexPath]的数组
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
}
- (NSString *)tableView:(UITableView *)tableView titleForDeleteConfirmationButtonForRowAtIndexPath:(NSIndexPath *)indexPath
{
return @"删除";
}
4.电子书阅读
用我的方法,就不用管电子书的宽高,它会自适应。
其实电视书是把一个UIScrollView加到控制器上,然后把pdfView加到UIScrollView上。
/*获取pdf文件*/
pdf = CGPDFDocumentCreateWithURL((CFURLRef)url);
/*获取文件总页数*/
pageCount = (int)CGPDFDocumentGetNumberOfPages(pdf);
-(void)drawInContext:(CGContextRef)context atPageNo:(int)page_no{
CGContextSetRGBFillColor(context, 1.0, 1.0, 1.0, 1.0);
CGContextFillRect(context,self.bounds);
CGContextTranslateCTM(context, 0.0, self.bounds.size.height);
CGContextScaleCTM(context, 1.0, -1.0);
if (self.pageNO == 0) {
self.pageNO = 1;
}
CGPDFPageRef page = CGPDFDocumentGetPage(self.pdfDocument, self.pageNO);
CGContextSaveGState(context);
CGAffineTransform pdfTransform = CGPDFPageGetDrawingTransform(page, kCGPDFCropBox, self.bounds, 0, true);
CGContextConcatCTM(context, pdfTransform);
CGContextDrawPDFPage(context, page);
CGContextRestoreGState(context);
}
四.其它总结
1.文件下载
ASIHttpRequest很好用的网络请求第三方。可以get,post请求,支持断点下载。
/**
* 下载文件
* @param urlStr 文件路径
* @param block 回调
*/
- (void)downloadFile:(NSString *)urlStr withType:(int)type finishCallbackBlock:(void (^)(BOOL result))block {
-
ASIHTTPRequest *request = [[ASIHTTPRequest alloc] initWithURL:[NSURL URLWithString:urlStr]];
NSString *filename = [urlStr lastPathComponent];
//下载后存储文件名
NSString *savePath = [MANAGER_FILE.CSDownloadPath stringByAppendingPathComponent:[NSString stringWithFormat:@"file/%@", filename]];
NSString *tempPath = [MANAGER_FILE.CSDownloadPath stringByAppendingPathComponent:[NSString stringWithFormat:@"temp/%@", filename]];
[request setDownloadDestinationPath:savePath];
[request setTemporaryFileDownloadPath:tempPath];
[request setShouldContinueWhenAppEntersBackground:YES];
[request setDownloadProgressDelegate:self];
[MANAGER_SHOW showProgressWithInfo:@"下载中..."];
[request setCompletionBlock:^{
[MANAGER_SHOW setProgress:1.0];
if ([MANAGER_FILE fileExists:savePath]) {
block(YES);
}else {
block(NO);
}
}];
[request setFailedBlock:^{
[MANAGER_SHOW setProgress:1.0];
block(NO);
}];
[request startAsynchronous];
}
2.get请求
/*
typedef void (^GetBackBlock)(id obj);
typedef void (^GetFailBlock)(NSError *error);
*/
- (void)doGetJson:(NSString *)urlstr withCompletionBlock:(GetBackBlock)completionBlock withFailBlock:(GetFailBlock)failBlock {
ASIHTTPRequest *request = [[ASIHTTPRequest alloc] initWithURL:[NSURL URLWithString:urlstr]];
[request startAsynchronous];
__block ASIHTTPRequest *_request = request;
[request setCompletionBlock:^{
switch ([_request responseStatusCode]) {//HTTP状态码
case 404://Not Found 无法找到指定位置的资源
case 500://Internal Server Error 服务器遇到了意料不到的情况
failBlock([_request error]);
break;
case 200://OK 一切正常
completionBlock([_request responseData]);
[MANAGER_SHOW dismiss];
break;
default:
failBlock([_request error]);
break;
}
}];
[request setFailedBlock:^{
failBlock([_request error]);
[MANAGER_SHOW dismiss];
}]
}
3.post传输
- (void)doPostJson:(PostModel *)model withSuccessBlock:(GetBackBlock)successBlock withFailBlock:(GetFailBlock)failBlock {
ASIFormDataRequest *request = [[ASIFormDataRequest alloc] initWithURL:[NSURL URLWithString:[model.urlStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]];
[request setRequestMethod:@"POST"];
for (NSString *key in [model.params allKeys]) {
[request setPostValue:[model.params objectForKey:key] forKey:key];
}
[request buildPostBody];
[request startAsynchronous];
__block ASIFormDataRequest *_request = request;
[request setCompletionBlock:^{
[MANAGER_SHOW dismiss];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.2f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
BLOCK_SUCCESS([_request responseData]);
});
}];
[request setFailedBlock:^{
[MANAGER_SHOW dismiss];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.2f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
BLOCK_FAILURE([_request error]);
});
}];
}