电子书单个章节

作者: 小石头呢 | 来源:发表于2019-02-26 07:59 被阅读10次

    项目源代码
    链接:
    https://pan.baidu.com/s/1I0HM3bSuqJbvAaI1XVZWeA 密码:2qkc

    主要代码:

    ViewController.m文件

    //
    //  ViewController.m
    //  电子书单章节
    //
    //  Created by 许磊 on 2019/2/16.
    //  Copyright © 2019年 xulei. All rights reserved.
    //
    
    #import "ViewController.h"
    #import "EBookView.h"
    
    @interface ViewController ()
    
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        //设置背景颜色
        self.view.backgroundColor = [UIColor brownColor];
        
        //创建一个电子书
        EBookView *bookview = [[EBookView alloc] initWithFrame:CGRectMake(0, 20, self.view.frame.size.width, self.view.frame.size.height-20)];
        
        //调取loadData方法获取文本内容
        bookview.text = [self loadData];
        
        //设置字体
        bookview.font = [UIFont systemFontOfSize:16];
        
        //添加
        [self.view addSubview:bookview];
        
    }
    
    #pragma mark -------loadData ---------
    //加载数据
    - (NSString *)loadData{
        //从文件读取数据
        //获取路径
        NSString *filePath = [[NSBundle mainBundle] pathForResource:@"登山拜师.txt" ofType:nil];
        //NSLog(@"%@",filePath);
        //返回文件的内容
        //NSString *file = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil];
        //NSLog(@"%@",file);
        return [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil];
    }
    
    @end
    

    EBookView文件

    //
    //  EBookView.h
    //  电子书单章节
    //
    //  Created by 许磊 on 2019/2/16.
    //  Copyright © 2019年 xulei. All rights reserved.
    //
    
    #import <UIKit/UIKit.h>
    
    //提供给外部访问的
    
    @interface EBookView : UIView
    
    /**接受外部传递过来的数据*/
    @property (nonatomic,copy) NSString *text;
    
    /**设置字体*/
    @property (nonatomic,strong) UIFont *font;
    
    /**是否需要动画*/
    @property (nonatomic,assign) BOOL animated;
    
    @end
    
    
    
    //
    //  EBookView.m
    //  电子书单章节
    //
    //  Created by 许磊 on 2019/2/16.
    //  Copyright © 2019年 xulei. All rights reserved.
    //
    
    
    
    #import "EBookView.h"
    
    //不希望给外部访问的方法或者属性就在.m文件里面声明
    
    /**翻页状态*/
    typedef enum{
        kAnimationTypeUp,
        kAnimationTypeDown
    } kAnimationType;
    
    @interface EBookView ()
    
    /**承载的文本*/
    @property (nonatomic,strong) UILabel *label;
    
    /**记录当前页数的索引值*/
    @property (nonatomic,assign) NSInteger currentPage;//无需赋值,升级版的int,默认为0
    
    /**保存每一页的范围*/
    @property (nonatomic,strong) NSMutableArray *pageRangesArray;
    
    @end
    
    @implementation EBookView
    
    //让这个类来管理电子书的操作和显示
    
    //创建一个类 让一个类继承于UIView
    //自定义一个控件 通常都是去重写initWithFrame方法
    //在这个方法里面实现视图的布局
    
    
    //重写initWithFrame方法
    -(instancetype)initWithFrame:(CGRect)frame{
        self = [super initWithFrame:frame];
        if (self != nil) {
            //设置label相关信息
            self.label = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, frame.size.width, frame.size.height)];
            _label.backgroundColor = [UIColor brownColor];
            _label.numberOfLines = 0;
            _label.font = self.font;
            [self addSubview:_label];
            
            //获取第一页相关信息 显示出来
            //不能在这里做 因为initWithFrame的时候text还没有数据
            
            //初始化数组
            self.pageRangesArray = [NSMutableArray array];
            
            //设置是否有动画
            self.animated = YES;
        }
        return self;
    }
    
    //重写set方法
    //用atext防止重复
    - (void)setText:(NSString *)atext{
        _text = [atext copy];
        
        //显示第一页的内容
        NSRange firstPageRange = [self caculatePageRange:_currentPage];
        self.label.text = [self.text substringWithRange:firstPageRange];
        
    }
    
    //重写font的get方法
    -(UIFont *)font{
        if (_font == nil) {
            //外部没有设置 给一个默认值
            self.font = [UIFont systemFontOfSize:16];
        }
        return _font;
    }
    
    #pragma mark -------caculatePageRange ---------
    //计算页数range 计算之前得确保是有数据的
    - (NSRange)caculatePageRange:(NSInteger)page{
        //定义一个NSRange结构体用于随时记录每一页的具体位置和长度
        NSRange range = NSMakeRange(0, 0);
        
        //第二页开始 将上一页的range拿出来
        if (page > 0) {
            //取出上一页,并转化为基本类型
            NSValue *rgValue = [self.pageRangesArray objectAtIndex:page-1];
            NSRange preRange = [rgValue rangeValue];
            //记录当前page的起始位置 = 上一页的起始位置 + 上一页的长度
            range.location = preRange.location + preRange.length;
        }
        
        //分割页面
        //每一次都是从需要计算的页面的起始位置开始计算
        for (int i = 0; range.location+range.length < self.text.length; i++) {
            
            //1.不断增加字符 (起始点,长度)NSRange(location length)
            range.length++;
            
            //2.获取当前这个range对应子字符串
            NSString *subString = [self.text substringWithRange:range];
            
            //3.计算subString的size(可以考虑抽出来一个方法)
            CGSize realSize = [subString boundingRectWithSize:CGSizeMake(self.label.frame.size.width, 20000) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName:self.font} context:nil].size;
            
            //4.判断是否超过这个显示的高度
            if (realSize.height > self.label.frame.size.height) {
                //进入了这个if里面,就说明下一页的第一个字符被算进来了
                //让range保存这一页的长度
                range.length--;
                
                //保存这个range
                NSValue *rgValue = [NSValue valueWithRange:range];
                [self.pageRangesArray addObject:rgValue];
                
                return range;
            }
        }
        
        //5.如果剩下的文本高度达不到一页,说明到了最后一页
        if (range.length > 0) {
            
            //保存这个range
            NSValue *rgValue = [NSValue valueWithRange:range];
            [self.pageRangesArray addObject:rgValue];
            
            return range;
        } else {
            //这个else部分完全不会触发,但是不写会报错
            return NSMakeRange(0, 0);
        }
        
    }
    
    #pragma mark -------touchesBegan ---------
    //触摸事件
    - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
        
        //获取UITouch对象
        UITouch *touch = [touches anyObject];
        CGPoint handlocation = [touch locationInView:self];
        
        //判断上一页还是下一页
        if (handlocation.x > self.center.x) {
            //下一页
            
            //判断是否到了最后一页
            NSRange currentPageRange = [[self.pageRangesArray objectAtIndex:_currentPage] rangeValue];
            if (currentPageRange.location+currentPageRange.length < self.text.length) {
                [self changePage:_currentPage+1];
            } else {
                NSLog(@"已经是最后一页了");
            }
        } else {
            //上一页
            
            //判断是否到了第一页
            if (_currentPage > 0) {
                [self changePage:_currentPage-1];
            } else {
                NSLog(@"已经是第一页了");
            }
        }
    }
    
    #pragma mark -------changePage ---------
    //切换页面
    //如果页面已经计算好直接获取 没有再次计算
    -(void)changePage:(NSInteger)page{
        
        NSRange range;
        
        //判断页面是否计算好放入数组中
        if (page > self.pageRangesArray.count -1) {
            //需要计算
            range = [self caculatePageRange:page];
            //动画
            if (self.animated) {
                [self animateWithType:kAnimationTypeUp];
            }
        } else {
            //之前已经计算过了 放入数组中了 直接拿出来使用
            range = [[self.pageRangesArray objectAtIndex:page] rangeValue];
            //动画
            if (self.animated) {
                [self animateWithType:kAnimationTypeDown];
            }
        }
        
        //确认当前页数
        self.currentPage = page;
        
        //显示页面内容
        self.label.text = [self.text substringWithRange:range];
    }
    
    #pragma mark -------animateWithType ---------
    //动画
    -(void)animateWithType:(kAnimationType)type{
        //开始动画
        [UIView beginAnimations:nil context:nil];
        [UIView setAnimationDuration:1];
        if (type == kAnimationTypeUp) {
            [UIView setAnimationTransition:UIViewAnimationTransitionCurlUp forView:self cache:NO];
        } else {
            [UIView setAnimationTransition:UIViewAnimationTransitionCurlDown forView:self cache:NO];
        }
        //结束动画
        [UIView commitAnimations];
    }
    
    
    @end
    
    运行结果

    附一:遇到的困难

    • 1.加入到项目中的文本无法读取

    解决方法: 在Targets -> Build Phases -> Copy Bundle Resources里面导入资源文件,运行项目

    示意图
    • 2.方法和属性声明的位置
      不希望给外部访问的方法或者属性就在.m文件里面声明,希望外部可以访问到的放在.h文件里面

    • 3.重写某些属性的set和get方法

    • 4.caculatePageRange函数的调用问题
      每一次调用caculatePageRange函数都是要计算每一页,也就是从每一页的第一个字符开始,最后一个字符结束。其中的for循环的结束条件不能为"i < self.text.length",这样的条件永远不可能正常结束循环,最后一页就会出现问题。应该改为"range.location+range.length < self.text.length"

    附二:个人改进点

    • 1.预加载页面
      初始状态只是加载了第一页,之后每加载一页都要计算一次,速度过慢。如果初始状态加载多个页面内容,每次点击切换页面都会继续加载页面。在不影响阅读体验的情况下,加载数据。

    • 2.外部可以访问数据的处理
      比如说文本字体font,不一定要在代码中设置,可以添加按钮等组合控件。达到在前端输入数据,后台接受数据达到控制字体的效果。(刷新)

    相关文章

      网友评论

        本文标题:电子书单个章节

        本文链接:https://www.haomeiwen.com/subject/uipveqtx.html