项目源代码
链接:
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,不一定要在代码中设置,可以添加按钮等组合控件。达到在前端输入数据,后台接受数据达到控制字体的效果。(刷新)
网友评论