iOS开发--电子书模块的设计制作

Posted xj_love

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了iOS开发--电子书模块的设计制作相关的知识,希望对你有一定的参考价值。

一.引言

从今天开始,在我的博客上开辟工作项目专栏,来总结记录项目中的开发难点。第一篇记录的是电子书模块。

二.项目需求

一级界面: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]);
        });
    }];
}

以上是关于iOS开发--电子书模块的设计制作的主要内容,如果未能解决你的问题,请参考以下文章

19基于STM32的电子打铃器

19基于STM32的电子打铃器

12基于STM32的电子密码器

首发福利!全球第一开源ERP Odoo系统架构部署指南 电子书分享

iOS-电子书开发 笔记

BLE MIDI开发 BLE MIDI 硬件电子乐器设备需要遵循的相关规范 ( 资料收集 )