Viewer 源码阅读心得1——存储

版权所有, 转载请著明出处,保留链接。
Viewer 源码阅读心得1
Viewer 是一个IOS平台上的PDF阅读器,其源码地址:https://github.com/vfr/Viewer

##存储概括
Viewer存储涉及到的类有ReaderThumbRequest, ReaderThumbCache, ReaderThumbFetch, ReaderThumbRender, ReaderThumbQueue,对外的接口是ReaderThumbCache,是个单例,而ReaderThumbFetch, ReaderThumbRendder, ReaderThumbQueue对外是不可见的。对外主要接口如下:

- (id)thumbRequest:(ReaderThumbRequest *)request priority:(BOOL)priority

其内部,大体是根据request在内存查找Thumb, ReaderThumbCache职责,如果没有找到,则交给ReaderThumbFetch, 从磁盘上加载thumb, 如果磁盘上不存在,则交给ReaderThumbRender, 从PDF文档中渲染出Thumb,保存到内存,保存到磁盘。其中从磁盘上加载,PDF中渲染,耗时较长,采用任务队列,异步加载、渲染图片,任务Operation由ReaderThumbQueue管理,而ReaderThumbFetch和ReaderThumbRender都是Operation的子类。其过程如下:

ReaderThumbCahce--->ReaderThumbFetch--->ReaderThumbRender-->ReaderThumbQueue

其中ReaderThumbRequest是把他们串起来的对象,在他们间流动,是一个输入输出参数,输入指的是请求Thumb的信息,输出指的是最终获取的thumb(UIImage)。

Read More

SDWebImage源码阅读心得

版权所有, 转载请著明出处,保留链接。

#SDWebImage源码阅读心得

##概括
SDWebImage是一个异步下载图片的开源模块。
实现的业务功能,是异步下载网络上的图片,为上层屏蔽实现细节,提供接口调用。

一张图片在Cocoa中用UIImage对象来表现,而要在视图上显示,则需要UIImageView,显然它是视图对象。其组合依赖关系如下:

UIImageView->UIImage->图像数据

那么最直接的想法,是在UIImageView视图上扩展一个接口(异步接口),当上层模块需要显示一张图片时,创建一个UIImageView的实例,再调用扩展的异步接口,传入图片的URL(获取图像数据),回调函数(异步调用完成函数)等参数,这样UIImageView的事情就做完了,上层模块想要把UIImageView显示到哪个视图,直接在该视图上调用addSubView即可, 当图像数据从网络上获取后, UIImageView自然会显示出该图像, 因为UIImageView已经为我们封装了图像显示功能,现在只欠缺的图像数据。这是一个最朴素的应用,当然上层模块如果在图像的异步请求中,突然不想要该图像了,那么,此时SDWebImage也提供了撤销的接口。

SDWebImage源码阅读后,最大的感受是其职责明确,即各个类功能单一,权责明确。类之间通过组合实现业务功能,通过Objective-C的Category(类别)来扩展基础框架Cocoa类的功能。

##代码结构
SDWebImage的主要代码结构如下:

SDWebImage                                ---> 主文件夹

    Categories                            ---> 对Cocoa框架中类的扩展
        UIImageView+WebCache            ---> UIImageView的扩展,对外接口,异步下载图像
        UIView+WebCacheOperation        ---> UIView的扩展,UIView实例关联任务Operation,对外接口,撤销任务用
        ......

    Utils
        SDWebImageManager                ---> 模块中中枢系统,各个类实例由它协调,提供单例接口
        ......

    Cache
        SDImageCache                    ---> 负责所有图片的存储,包括内存上的缓存、硬盘文件存储,提供单例接口

    Downloader
        SDWebImageDownloader            ---> 负责模块中所有图片的下载,提供单例接口
        SDWebImageDownloaderOperation    ---> 负责模块中一张图片的下载,即一个任务,继承自Operation

    .....

其主要脉络来自于UIImageView+WebCache, SDWebImageManager, SDImageCache, SDWebImageDownloader, SDWebImageDownloaderOperation, 至于涉及图片的部分,例如格式,编码,resolution都没有深究。

Read More

FlyBook——飞书推荐书源

电子书在不同国家地址的版权法各有差异, 本APP尊重知识产权和版权法,故不默认预设书籍资源,用户可以自主添加。为了方便用户,下面列出了一些常见的网上的书库,可供参考。

##网盘
百度云
微盘 [网页需要在电脑版浏览下,才能下载]
Dropbox[中国区需要翻墙]

##中文
子乌书简
书仓
新浪共享[已关闭]
掌上书院
COAY [已关闭]

##论坛
Kindle人社区
Kindle114

##英文
Baen
Gutenberg

##俄文
zone4iPhone.ru

##法语
Revues.org
Atramenta
YouScribe

以上书库都是独立运作的第三方网站,与本APP无关系,相关网站问题请直接与该站管理员联系。

另如果大家有新的,好的书库推荐,请直接在评论处留下:
OPDS格式: URL#书库名称, 例如:http://www.shucang.org/s#书仓
网址格式:URL#地址名称,例如: http://vdisk.weibo.com#微盘
这样大家可以直接在FlyBook推荐书源中点击链接加到FlyBook中。

常用命令记录

GIT

git tag #列出所有tag;
git tag –help #tag的帮助文档;
git tag -d [tagname] #删除指定名称的tagname;
git push –tag #将本地tag推送的远程库;

git clone [email protected] #克隆一个远程库到本地
git init #初始化一个本地库
git remote add name url #添加一个远程仓库路径 名称为name, 地址为:url
git remote -v #列出所有的远程仓库
git pull name #把本地的变化推送到远程仓库name中

git checkout tag_name #在当前分支上 取出tag_name的版本

git fetch origin master #从远程origin库上下载master分支

git log -n #查看最近的n次提交

##XCODE
CMD+SHIFT+ALT+左箭头 #代码折叠

##SUBLIME
CMD+SHIFT+P #调出命令面板

##PODFILE

pod install #安装Podfile中未安装的依赖库;
pod update #更新Podfile中所有的依赖库;
pod search libName #查找指定名称的库;

##HEXO-博客

hexo new article_title #hexo 创建一篇新的博客

使用core text制作ios杂志类app向导

翻译自:core text tutorial for ios: making a magazine app. 版权所有, 转载请著明出处,保留链接。

始于iOS 3.2+ 和OSX 10.5+,Core Text一个模板engine, 使我们能细粒度的控制文本的格式和展现。

它正好介于UIKit 和 Core Graphics/Quartz之间:

  • 在UIKit中,有UILabel控件,使用IB可以轻易地拖拽控件,在屏幕上加字或文本行,但不能改变每个字的颜色。
  • 在Core Graphics/Quartz中,可以做系统能做的每一件事,但是需要计算文本中每个符号的位置,然后把它画在屏幕上。
  • Core Text在两者之间。能完全控制位置,布局,颜色大小等属性,也能隐藏其他处理细节,例如换行和字体渲染等。

如果你做一个杂志或书的app,Core Text 将非常有用,它在iPad上表现非常好。

本文是iOS Core Text的Tutorial, 会利用Core Text 开发一个简单的杂志App(Zombies), 本向导会向你展示这个开发过程。

你将会学到:

  • 在屏幕上展示格式化的文本;
  • NICE文本的appearance;
  • 在文本内容中加入图片;
  • 做一个杂志app, 通过加载文本标志控制渲染文本的格式;

为了理解Core Text, 首先你需要了解基本的iOS开发知识。如果你是iOS的新手,你最后先读读本网站的其他向导

Read More

iOS SDK 在app中增加发送邮件功能

#iOS SDK 在app中增加发送邮件功能

翻译自 iOS SDK: Send E-mail In-App. 版权所有, 转载请著明出处,保留链接。

在这篇ioS开发向导中,我会演示使用MFMailComponseViewController类来发送邮件,而不需要离开app。我们会建立关于收件人,邮件主题,邮件内容,甚至附件的邮件模板。

在应用程序中,iOS SDK提供了一个简单,标准的接口让用户了发送和编辑邮件,即MFMailComposeViewController类。这个视图控制器呈现一个标准的邮件接口,并且提供了反应用户操作事件功能接口。例如,当用户点击“发送”或“取消”时,这个类会收到消息并通知你。

注意:MFMailComposeViewController只有在iOS 3.0以后的版本中才能用。

那么,它是怎么工作的呢?请跟随本向导的脚步。

Read More

巍巍的高山

###巍巍的高山###

冬日暖阳,天空清晰,晌午,父亲、母亲、妻子、我一同走在路上,去往村外的田地,挖荸荠。

父亲独自扛着锄头走在前头,母亲提着篮子,妻子紧随母亲,而我拖拉在后。

路程过半,母亲回过身,踉踉跄跄到我这边,煞有介事但带笑地,指着远处山腰跟我说:

你看,那就是老年坟陵,村里建的,我和你爸定了两座,已经交过钱。

我轻轻嗯了声,没有接话,看了看不远处那座崭新的坟陵。

巍巍的高山

LTCoreText

##AppDelegate
生命周期函数

  • (BOOL)application:(UIApplication )application didFinishLaunchingWithOptions:(NSDictionary )launchOptions
    1)把程序束中的html文件拷贝到documents目录下.
    2)构建self.window, self.mainViewController.
    3)设置self.window.rootViewController.

##MainViewController
生命周期函数

  • (id)init;初始化函数.
    1) 从MainViewController.nib文件构建MainViewController实例.
    2) 构建self.landscapeView, self.portraitView实例, 都是LTTextView实例.
    3) 在self.landscapeView, self.portraitView上添加tap手势.
  • (void)viewDidLoad
    1) 根据横屏还是竖屏,隐藏self.landscapeView还是self.portraitView.
    2) 设置self.landscapeView , self.portraitView的frame.
    3) 把self.landscapeView, self.portraitView作为子视图添加到self.view.

-(void)flipsideViewController:(FlipsideViewController )controller didSelectFileWithPath:(NSString )path
1)当点击某html文件时,会调用这个函数,就从这个函数开始分析.
2)调用[self intertHTMLFileWithPath: atIndex:]

  • (void)intertHTMLFileWithPath:(NSString*)path atIndex:(NSUInteger)index
    1). dispatch_async 异步执行如下动作:
    2). 以html文件的路径加载数据到NSData.
    3). 设置字典options, NSBaseURLDocumentOption, DTDefaultLinkDecoration的为键的值.
    4). 以加载进来的htmlData, options为参数构建一个属性字符串 attrString.
    5). 如果NSUserDefaults中有”lColVText”的值,则在属性字符串中添加属性kCTVerticalFormsAttributeName.
    6). 以属性字符串, frameSize(1024,748)构建LTTextLayouter的实例,即landscapeLayouter.
    7). 设置landscapeLayouter的columnCount, verticalText, contentInset, columnSpace, justifyThreshold的值.
    verticalText是设置布局者,而lColVText是设置属性字符串。
    8). 调用landscapeLayouter 的 layoutIfNeeded 方法.
    9). 如法炮制portraitLayouter.
  1. 在主线程中调度 如下动作:
    [landscapeView insertLayouter: atIndex:] 在视图中插入landscapeLayouter布局.

text view delegate委托
-(UIView )textview:(LTTextView )textView viewForRunDictionary:(NSDictionary *)dict
1) 从参数dict中获取DTTextAttachment, 以NSAttachmentAttributeName为key.
2) 构建一个LTTextImageView实例imageView.
3) 把attachment的contentURL赋予imageView的imageURL, attachment的displaySize赋予imageView的displaySize.
4) [imageView startDownload] 开始异步加载图片.
5) 返回imageView.

##LTTextImageView 图像视图 继承于UIImageView
公共属性:

  1. imageURL 超连接
  2. displaySize 显示大小.

生命周期函数

  • (id)initWithFrame:(CGRect)frame 初始化函数
  1. self.userInteractionEnabled 用户交互标志设置为YES.
    self.contentMode 内容模式设置为UIViewContentModeScaleAspectFit
  2. 创建overlayView 视图,并作为子视图添加.
  3. 在overlayView视图上添加UITapGestureRecognizer手势.

-(void)layoutSubviews 布局子视图.

  1. 调用父类的layoutSubviews.
  2. 获取overlayView, 根据viewTag, 设置overlayView的frame.

-(void)startDownload 公共函数

  1. 构建一个选项字典 borderColor, borderWidth.
  2. 获取LTImageDownloader的一个实例,调用 downloadImageWithURL:imageBounds:options:completion:
  3. 待图像下载完成,设置self.image, 并设置alpha = 1.0 动画效果.

##LTImageDownloader 图像下载器 继承于NSObject

公共函数
+(id)sharedInstance; 获取图像下载器的共享实例.

  1. 利用dispath_once 调用初始化函数 构建一个实例.
  • (id)init 初始化函数
  1. 调用父类的init方法
  2. 利用函数dispatch_queue_create 创建一个调度队列.
  3. NSOperationQueue 创建一个操作队列的实例,并设置最大的并发操作数为3.
  4. NSCache 构建一个图像缓存,最大数为30.

-(void)downloadImageWithURL:(NSURL )url imageBounds:(CGSize)bounds options:(NSDictionary )options completion:(LTImageDownloadCallback)comp

  1. 在调度队列里里异步调度,执行如下操作.
    1). 以传入参数url为依据构建缓存key.
    2). 从缓存中以key获取缓存的图像数据.
    3). 如果存在key的数据,则进行如下操作:
    1). 以缓存的数据构建一个UIImage对象。
    2). 以bound为参数调整图像的大小。
    3). 异步等待主线程,在主线程中调用完成block.
    4). 返回.
    
    4). 以url为参数构建一个NSURLRequest实例.
    5). NSURLConnection 发起一个异步的请求,以req,downloadQueue为参数,当请求完成时,执行如下操作
    1). 如果有数据返回,且没有发生错误
    2). 则现在_imageCache中缓存数据。
    3). 在以此数据为依据构建一个UIImage实例,调整大小,异步等待主线程调用完成块block.
    
    6). 如果发生错误,则完成块传入nil,error.

私有函数

  • (UIImage)resizeImage:(UIImage)image bounds:(CGSize)bounds options:(NSDictionary*)options 调整图像大小
  1. 如果bounds为0,则直接返回,不需要调整大小。
  2. 如果bounds.width不为0, 则以bounds.width为依据,调用sizeThatFitsKeepingAspectRatio, 计算目标大小,否则以bounds.height为依据.
  3. 把图像画在图像上下上,在从上下文中获取图像,返回.

##LTTextFrame 对CTFrame的包装.

  • (CGRect)frameWithGlyphRuns:(NSArray*)runs onLine:(CTLineRef)line
    获取run的矩形区域.
    1). 根据参数line获取行索引,根据行索引获取行的起始位置lineOrigin.
    2) 遍历参数数组runs. 做如下操作:
    1. 设置初始化ascent = 0, descent = 0, leading = 0
    2. 调用CTRunGetTypographicBounds从run中获取ascent, descent, leading, width.
    3. 调用CTRunGetPositions获取run的位置到p.
    4. 调用CTRunGetAttributes获取run的attr
      获取attr中的kCTVerticalFormsAttributeName为key的值,如果有该属性,则设置xoofs = -p.y - leading - descent; 不然为p.x.
    5. 以获取的参数构建runFrame
      3). 返回rect.

##LTTextLayouter 继承于NSObject. 文本布局.
私有属性:
1) _attributedString 属性字符串.
2) _frameSize 显示窗大小.
3) _options 字典.
4) _columnCount 栏数.
5) _needFrameLayout 布局标志.
6) _framesetter CTFrame的工厂 CTFramesetterRef.
7) _frames 显示窗的数组, 数组的数组
8) _attachments
9) _contentInset 内容区域的外边缘 UIEdgeInsets类型.
10 _columnSpace 栏与栏之间的距离.
11 _backgroundColor 背景色
12 _verticalText 是否是竖直文字标志

生命周期函数
-(id)initWithAttributedString:(NSAttributedString )attrString frameSize:(CGSize)size options:(NSDictionary)options;
1). 设置_attributedString, _options, _frameSize
2). 以属性字符串为参数 构建 _framesetter.
3). 设置默认的backgroundColor, contentInset, columnSpace, justifyThreshold, useHyphenation,.
4). 设置_columnCount为1.

  • (void)layoutIfNeeded; 调用 [self layoutFrame] 布局窗口.

  • (void)layoutFrame; 把属性字符串—>_frames上.
    1). _frames 初始化一个可变数组实例.
    2). 获取属性字符串_attributedString的长度.
    3). currenFrames 可变数组实例 存储当前页窗口.
    4). 进入如下循环:
    1) contentFrame, 根据curentFrames中对象的个数,内容显示窗,即矩形区域: [self columnFrameWithColumn:]
    2) frameBounds 从contentFrame计算.
    3) path CGPathRef实例, 从frameBounds计算.
    4) 以framesetter, strRange, path, frameAttr为参数通过函数CTFramesetterCreateFrame构建CTFrameRef的实例frame.
    5) 构建LTTextFrame实例, 并设置属性frame(CTFrameRef实例), contentFrame, frameBounds, verticalLayout标志.
    6) LTTextFrame加入到currentFrames数组中.
    7) 如果当前页面已满,则把curentFrames加入_frames数组中,并重新构建一个数组对象赋予currentFrames。
    8) 从frame中获取当前显示的字符范围:CTFrameGetVisibleStringRange, 释放frame对象.
    9) 累加visibleRange 到strRange的location.
    5) [self createAttachmentsArray] 创建附加数组。
    1) 创建_attachments数组.
    2) 对_frames中每个数组(frames)遍历:

    1)创建一个dstarrayPage数组对象,添加到_attachements
    2)遍历frames中的每个frame, dstarrayPage添加一个空对象(NSNull).
    
  • (void)lt_frameDraw:(CGContextRef)context pathBBox:(CGRect)pathBBox lines:(CFArrayRef)lines lineOrigin:(CGPoint *)lineOrigin
  1. 遍历行数组lines中的每一行,进行如下操作:
  2. 保存绘图上下文,设置当前文本矩阵为单位变换矩阵,设置文本位置为0,0位置。平移坐标轴到每一行的开始位置。
  3. 获取行line, 以及行所占的矩形区域lineBounds.
  4. 如果有连字符标志,则进行如下操作:
    1). 获取行的字符串范围cfStringRange. 并以此构建一个NSRange对象的范围.
    2). 连字符为0x00AD,根据行字符串的范围,在属性字符串中获取行的最后一个字符。
    3). 如果最后一个字符等于连字符,则执行如下操作:
    重新构建一行属性字符串,并把最后的一个字符用"-"替代.
    并重新创建行,两端对齐行,调用CTLineDraw(...)画在绘图上下中.
    
    4). 否则,则直接调用CTLineDraw(…)把行画在绘图上下文中.
  5. 如果没有连字符标志,则进行如下操作:
    1). 如果有垂直文字的标志,且两端对齐比例不为1,则利用函数CTLineCreateJustifiedLine创建两端对齐行,画出来,以height为行空间.
    2). 如果没有垂直文字的标志,且两端对齐比例不为1, 则利用函数CTLineCreateJustifiedLine创建两端对齐行,画出来,以width为行空间.

-(void)drawInContext:(CGContextRef)context atPage:(NSUInteger)page
1). 判断_needFrameLayout是否需要布局,如果是则调用 [self layoutFrame] 进行布局.
2). 判断传入参数page是否大于self.pageCount总页数,如果大于,则直接返回.
3). 从_frames数组中获取page处的数组frames
4). 遍历数组frames中的每个frame(textFrame), 进行如下操作.
1). 从textFrame中获取CTFrameRef
2). 从CTFrameRef中获取行数组lines.
3). 从textFrame中获取内容存放的矩形区域pathBBox.
4). 保存当前上下文。
5). 如果是画垂直文本的,则执行如下操作:
1). 逆时针旋转90度,x坐标轴向负方向平移-_frameSize.height.
在x坐标轴向正方向平移pathBBox.origin.y, y坐标轴向正方向平移pathBBox.origin.x.
6). 如果不是垂直文本的,则x坐标轴向正方向平移pathBBox.origin.x, y坐标轴向正方向平移.
7). 如果是使用连字,或者两端对齐比例不为1, 则调用 [lt_frameDraw:pathBBox:lines:lineOrigin:] 逐行渲染.
8). 否则,直接调用CTFrameDraw(..)把布局窗渲染到画布上。
9). 恢复绘图的上下文.

公共函数

  • (NSArray*)attachmentsAtPageIndex:(NSUInteger)index column:(NSUInteger)col;
    某页某栏上的附件.
  1. 判断是否需要布局frame, 如果需要, 则[self layoutFrame]
  2. 获取某页某栏上的附件数组,从_attachments.
  3. 如果该数组是NSNull类的实例,则重新构建一个数组实例赋予.
  4. 获取该页该栏的LTTextFrame的实例.
  5. 调用[self storeFrameOfAttachment:textFrame to:attachments], 把该frame上的附件保存到该数组.
  6. 把_attachments中的NSNull实例填充位置替换成attachments.
  • (void)storeFrameOfAttachment:(LTTextFrame)frame to:(NSMutableArray)dst
    保存LTTextFrame上的attachement到一个数组.
  1. 遍历frame中的每一行line。
  2. 遍历line中每一个run。
  3. 获取run的属性字典, 如果属性字典中包含”kCTRunDelegateAttributeName”的key,则执行如下动作:
    1). 获取该run的attachFrame的矩形区域,调用 frameWithGlyphRuns : onLine
    2). 以该run的属性,frame为构建一个字典存储在dst数组中.

##帮助函数

  • (CGRect)columnFrameWithColumn:(NSUInteger)col

  • (CGSize)columnSize

##LTTextView 继承于UIScrollView 文本视图
私有属性:

  1. _layoutMode 布局模式
  2. _layoutModeChanged 布局模式改变了标志.
  3. _framesizeChanged 显示窗口改变了标志.
  4. _layouters 数组 存储布局.
  5. _loadedViews 数组 加载了的视图.
  6. _pageCount 页数
  7. _currentState 结构体 当前状态.
  8. _currentScrollIndex 当前滚动的索引.
  9. _scrollIndexChanged 滚动索引变动了的标志.

生命周期函数

  • (id)initWithFrame:(CGRect)frame.
    1) 调用父类的initWithFrame.
    2) 设置_framesSizeChanged, autoresizingMask, pagingEnabled, backgroundColor, autoresizeSubviews, scrollToTop, deletegate.
    3) 创建_layouters, _loadedView的数组
    4) 设置_currentScrollIndex, _layoutMode.

公共函数

  • (void)insertLayouter:(LTTextLayouter*)layouter atIndex:(NSUInteger)index; 插入布局.
    1). 如果index 大于_layouters数组中对象的个数,则令index为数组的长度.
    2). 调用[self layouterAtScrollIndex: pageIndexOnLayouter:]获取当前的文本布局者和页码索引.
    3). 在_layouters的index索引处插入layouter。
    4). 调用[self loadedViewsArrayCount:] 加载视图
    5). 加载layouter的视图,并添加到_loadedviews中.
    6). 调用self calculatePageCount 计算页数
    7). 计算滑动视图即self的内容区域 调用函数[self contentSizeForCurrentLayoutMode]
    8). 调用[self recreateTextViews] 重新构建文本视图.
    9). 计算self.contentOffset 滚动到指定页.

帮助函数

  • (LTTextLayouter)layouterAtScrollIndex:(NSUInteger)index pageIndexOnLayouter:(NSUInteger)indexOn
    1). 如果传入参数index + 1 大于_pageCount, 则返回nil.
    2). 依次遍历_layouters数组中的每个layouter对象, 获取其页数,并累加到count, 如果count大于index, 则说明找到了页码所在的layouter, 记录在该布局下的页码,并返回layouter.
  • (NSMutableArray*)loadedViewsArrayCount:(NSUInteger)count.
    1) 根据页数创建可变数组arrary, 再在数组中填充NSNull实例.返回array数组对象.
  • (void)calculatePageCount 计算页数. 累加每个layouter的页数 设置到_pageCount上.
  • (CGSize)contentSizeForCurrentLayoutMode. 计算内容区域.
  1. 依据布局模式_layoutMode 计算contentSize.
  • (UIView*)hasViewWithScrollIndex:(NSUInteger)scrollIndex.
    根据滚动索引判断是否有视图.
  1. 根据滚动索引计算frame矩形区域. [self frameWithScrollIndex:].
  2. 如果frame为CGRectZero, 立即返回nil对象.
  3. 根据scrollIndex,获取layouter, pageIndex.
  4. 从_loadedViews中获取obj,如果obj是NSNull的实例,则返回nil。
  5. 设置pageView的frame.
  • (UIView*)viewWithScrollIndex:(NSUInteger)scrollIndex.
    根据scrollIndex创建view.
  1. 根据滚动索引计算frame矩形区域. [self frameWithScrollIndex:].
  2. 如果frame为CGRectZero, 立即返回nil对象.
  3. 根据scrollIndex获取layouter, pageIndex.
  4. 根据frame, layouter, pageIndex为参数创建LTTextPageView的实例pageView.
  5. _loadedView数组的数组中替换NSNULL的实例为pageView.
  • (void)recreateTextviews. 重新创建文本视图
    创建前后一页在scrollView上, 缓存前后3页在数组中,
    1). 如果本视图hidden, alpha 是隐藏的,则立即返回。

2). 创建一个可变数组实例viewsToUse.
3). i=0,1,2, 做如下事情: 获取滑动索引的前后中三个视图.
1). 根据当前滚动索引计算index索引.
2). 如果index 小于_pageCount,则做如下操作:
1) 判断当前index的view是否缓存,并返回. [self hasViewWithScrollIndex:]
2) 如果没有缓存,则依据索引返回一个pageView. [self viewWithScrollIndex:]
3) 如果有,则pageView 从父视图上移除
4) 把pageView加入到viewsToUse数组中.

4). 创建一个可变数组实例scrollIndexToUse.
5). i=0,1,2…6 做如下事情: 前后7页缓存着.
1). 根据_currentScrollIndex 计算index, 即_currentScrollIndex的前后6个索引.
2). 在scrollIndexToUse数组中插入该索引.

6). 对_loadedViews中的每个数组views进行遍历
遍历views中的每个视图pageView, 计算当前视图的scrollIndex,
如果scrollIndex不在scrollIndexToUse中,则移除该pageView, 并在Views中用NSNull 的实例替代.

7). 对self.subViews每个子视图进行遍历,看是否在viewsToUse数组中,如果不在,则从父视图中移除.
8). 对viewsToUse中的视图进行遍历, 作为子视图添加到self.view, 并且移动子视图到末尾.

##LTTextPageView 页视图
私有属性

  1. _layouter 文本布局
  2. _index 页码.
  3. _isNeedShowAttachments 是否需要显示附件.
  4. _imageSizes 数组
  5. _imageDownloaded 图像下载标志.
  6. _imageView 图像视图字典.
  7. _zoomedImageView 放大视图 LTTextImageScrollView实例.

生命周期函数.

  • (id)initWithFrame:(CGRect)frame layouter:(LTTextLayouter*)layouter pageIndex:(NSUInteger)index.
  1. 设置self.autoresizingMask, autoresizesSubviews
  2. 设置_layouter布局, _index 页码索引.
  3. 设置self.opaque, self.clearsContextBeforeDrawing 标志.
  4. 设置 _isNeedShowAttachments 标志为YES.
  • (void)drawRect:(CGRect)rect 重载了父类方法.
  1. 获取当前绘图上下文: UIGraphicsGetCurrentContext().
  2. 设置绘图上下文中的FillColor.
  3. 设置绘图上下文的FillRect。
  4. 调用[self textView] 获取文本视图(LTTextView的实例), 即页视图的文本视图.
  5. textView的textViewDelegate委托(即MainViewController控制器) 有实现方法textview:willDrawPageIndex:inContext:, 则调用之, 再设置CGContextSetTextMatrix 文字变化矩阵, 设置文字的绘画位置点.
  6. CGContextScaleCTM 翻转Y坐标轴.
  7. CGContextTranslateCTM 移动Y坐标轴.
  8. 异步调度 在主线程中 调用[self showAttachmentsIfNeeded] 显示附件(多媒体数据).
  9. 如果textView的textViewDelegate委托实现了textview:didDrawPageIndex:inContext:方法. 则再翻转Y坐标,设置文本变换矩阵,文本位置,调用之.
  10. 调用布局_layouter 的[self drawInContent atPage:]把_index页面画在

帮助函数

  • (LTTextView*)textView 返回LTTextPageView的父视图.

  • (void)showAttachmentsIfNeeded 显示附件如果需要.

  1. 如果_isNeedShowAttachments为YES,则设置为NO.
  2. 获取页面的textView.
  3. 调用布局_layouter 的columnCountAtPageIndex 获取当前页面的栏目数.
  4. 对每一个栏, 调用布局_layouter的columnFrameWithColumn 获取栏的Frame.
  5. 根据页码索引_index, 栏索引, 调用布局_layouter 的attachmentsAtPageIndex:column获取包含附件的数组属性
  6. 对在数组属性中的每一字典,做如下操作:
    1) 获取frame 的矩形区域.
    2) 按栏的位置进行偏移后的frame.
    3) 调用textView的textViewDeletegate的委托的textview:viewForRunDictionary:获取附件的view.
    3) 设置view的autoresizesSubviews, autoresizingMask, frame的属性.
    4) 把该view作为子视图添加.

总结

  1. MainViewController(视图控制器),
  2. LTTextView(视图 继承于UIScrollView,书视图),
  3. LTTextPageView(视图, 页面视图),
  4. LTTextImageView(图像视图,用户显示attachment),
  5. LTImageDownloader(图像下载器),
  6. LTTextLayouter. 布局者.

构建
–MainViewController —> LTTextView

  1. 在MainViewController的init初始化方法中构建LTTextView的实例,以CGRectZero为参数.
  2. 在MainViewController的viewDidLoad函数中设置LTTextView的frame(设置标志,_framesizeChange为YES), 并添加到控制器视图的子视图.

–MainViewController —> LTTextLayouter
在MainViewController的- (void)intertHTMLFileWithPath:(NSString*)path atIndex:(NSUInteger)index方法中.

  1. 获取属性字符串,设置属性字符串;
  2. 构建LTTextLayouter实例,设置其属性;
  3. 调用其layoutIfNeeded 进行布局;
    触发layoutFrame函数,[构建每个布局窗,字型].
  4. 再把LTTextLayouter插入到LTTextView,把两者关联起来
    -(void)insertLayouter:(LTTextLayouter *)layouter atIndex:(NSUInteger)index

–LTTextView —> LTTextPageView

  1. 在LTTextView的insertLayouter函数中,调用[self recreateTextViews];
  2. 在此函数中会创建LTTextPageView,在[viewWithScrollIndex:]函数中把LTTextPageView与LTTextLayouter关联起来。
    通过LTTextPageView [initWithFrame:layouter:page:]初始化函数。
  3. 把LTTextPageView添加为子视图。并做缓存处理, 添加前后三个LTTextPageView到LTTextView, 缓存7个LTTextPageView.

渲染
– LTTextView —-> LTTextImageView/LTTextPageView
LTTextView 的layoutSubviews方法

  1. 调用父类的layoutSubviews方法。
  2. 调用[self framesizeChanged]
    1). 移除self.subviews中的LTTextPageView.
    2). 计算页数,赋值给_pageCount.
    3). 计算contentSize, 使LTTextView可以滑动。
    4). 如果布局模式发生了变化,则计算contentOffset, 赋值给self.contentOffset.
  3. 如果当前滚动索引不等于_currentScrollIndex或者framesize发生了变化, 则调用[self layoutPages]
    1). 计算当前滚动索引,调用 [self currentScrollIndex]
    2). 调用 [self recreateTextviews]
    3). LTTextPageView showAttachmentsIfNeed 方法, 显示多媒体.

– LTTextPageView —> LTTextLayouter
LTTextPageView 的(void)drawRect:(CGRect)rect方法.
调用LTTextLayouter [drawInContext:atPage:] 把某页渲染到上下文中。

在iOS应用程序中使用自定义字体

翻译自:Using Custom fonts in your iOS application. 版权所有, 转载请著明出处,保留链接。

关于app中的自定义字体,可以在网上搜到几篇博文,但是可惜的是,没有一篇能帮助我完成自定义字体的,大多数博文,人云亦云,误人子弟。基于此,有必要介绍怎样在app中使用自定义字体。

首先,你必须意识到自定义字体是基于应用程序讲的,而不是基于iOS设备的,即你不能认为为iOS设备添加了一款字体,就认为为该iOS设备上的所有app都添加了该款字体,而是,你需要为ios设备上的每一个app都添加这款字体。

其次,在iOS应用程序中,包括UIWebView的应用程序,在使用自定义字体或者是QuickConnectFamily中的字体时,不需要写一行代码。

第三, 在应用程序中不需要调用@font-face CSS。

第四, 本文的示例会介绍两个自定义字体的添加和使用步骤。分别是Freshman字体和PF Handbook Pro Normal 字体,它们都可以以ttf文件的形式使用。

Read More