iOS开发--从TQRichTextViewDemo中学会分析工程

下载地址:

http://code4app.com/ios/TQRichTextView/5244fe9c6803fa0862000000

1.首先找到AppDelegate类,无论一个工程有多么复杂,多么多的类,但入口只有一个就是AppDelegate类的didFinishLaunchingWithOptions方法。我们阅读别人源代码的时候可以从这里入手。在TQRichTextViewDemo工程中,这个方法中仅创建了一个TQViewController并设置为window的rootViewController。

2.去看TQViewController的实现。首先也是从初始化方法开始,没有initWithNib,那么就看viewDidLoad。在这个方法中,只创建了一个TQRichTextView,设置了frame,text,backgroundColor,delegate等属性,然后添加到了self.view上。看完viewDidLoad方法,发现这是系统调用的最后一个方法,那么结合模拟器的运行效果,我们可以猜测,文字的解析和绘制过程被封装到了TQRichTextView中。

3.去看TQRichTextView。

  1. 浏览一下头文件,了解有哪几个属性和可调用的方法。
  2. 阅读initWithFrame方法,发现这里只是对几个属性的初始化。
  3. 寻找是否有重写的set,get方法。发现setText,setFont,setTextColor,setLineSpacing方法。阅读发现,每次更新完值后会调用setNeedsDisplay方法。说明,每次对这四个对象赋值后都会调用drawRect。
  4. 继续阅读.m文件,发现drawRect方法,这个也是有系统调用的,那么绘制的工作应该是在这里完成。阅读drawRect方法。
    1. 在drawRect中首先调用了analyzeText方法,传入了参数_text,并把返回值赋给了_textAnalyzed。

      1. 去看analyzeText方法。首先清空了数组和字典,然后定义了一个NSString类型的result变量和NSMutableArray类型的array并赋值为richTextRunsArray。
      2. 执行TQRichTextEmojiRun的一个方法,传入string和array的指针。
        1. 因为这个方法是TQRichTextEmojiRun的类方法,所有我们直接去看该方法,而不用去看TQRichTextEmojiRun的初始化方法。连蒙带猜的我们可以确定这个方法应该是解析字符串中表情的方法。看重点,发现如果解析到表情,那么就创建一个TQRichTextEmojiRun的队形,range=表情字符串在string中的range,originalText=表情字符串,把这个创建的对象放入传进来的数组指针中,最后替换传入字符串中的表情字符串为空格,并返回被替换过表情的字符串。
        2. 通过看TQRichTextEmojiRun的init方法,发现初始化时设置了两个变量,来自父类TQRichTextBaseRun,分别设置了TQRichTextRunType和是否响应触摸。发现TQRichTextURLRun也继承自TQRichTextBaseRun。
        3. 跳出这个方法,回到analyzeText中,继续往下看。
      3. 执行TQRichTextURLRun的一个方法,传入result和array的指针。
        1. 同上,直接去看这个方法的实现。从和TQRichTextEmojiRun的命名格式类似,调用的方法名类似,我们可以猜测这个方法是解析URL的,那么就直接看重点。发现,如果找到匹配的字符串,就创建一个TQRichTextURLRun的对象,设置range=URL的range,originText = URL,然后把对象添加到传进来的数组中,最后返回string。
        2. 通过看TQRichTextURLRun的init方法,发现同上初始化时设置了两个变量,来自父类TQRichTextBaseRun,分别设置了TQRichTextRunType和是否响应触摸。发现TQRichTextEmojiRun继承自TQRichTextImageRun,TQRichTextImageRun继承自TQRichTextBaseRun。
        3. 跳出这个方法,回到analyzeText中,继续往下看。
      4. 遍历richTextRunsArray中的对象,每个对象调用setOriginalFont方法,传入参数self.font。通过阅读上面的两个方法的内部实现明白richTextRunsArray中保存的是TQRichTextEmojiRun和TQRichTextURLRun对象,所以去看这两个对象的setOriginalFont方法。
        1. 这个设置的是继承自父类TQRichTextBaseRun的属性。
        2. 回到analyzeText继续往下看。
      5. 返回result,跳出这个方法,继续看drawRect。
    2. 接下来是创建NSAttrbutedString,并赋值一些属性。
    3. 文本处理,遍历richTextRunsArray中的对象,每个对象调用replaceTextWithAttributedString方法。
      1. 在TQRichTextEmojiRun没有找到replaceTextWithAttributedString方法,那就去TQRichTextImageRun中找,阅读该方法。

        1. 传入的attrString删除占位的空格字符。
        2. 创建CTRunDelegateCallbacks,返现设定的宽高为OriginalFont的高度的1.1倍。
        3. 创建空格NSAttrbutedString对象,添加CTRunDelegateRef属性。
        4. 把空格NSAttrbutedString对象插入至传入的attrString。
        5. 查看super方法,发现在这段range上还设置了一个键值对,key为TQRichTextAttribute,value为自己。
      2. 同上阅读TQRichTextURLRun的replaceTextWithAttributedString方法。
        1. 为该段range的文字添加蓝色字体。
        2. 调用super方法,为该段range添加同上的key-value键值对。
      3. 返回继续阅读drawRect。
    4. 下面是绘制的准备工作,不想研究咋绘制的粗读就行。
    5. 清空richTextRunRectDic
    6. 绘制,不想研究的也可粗读略过。
    7. 找重点,绘制替换过的特殊文本单元。
      1. 遍历取出每一个run,取出run的attributes中key为TQRichTextAttribute的值。
      2. 如果值存在,则说明,这个run是特殊的run。
      3. 求run的frame,粗读略过。
      4. 调用drawRunWithRect方法,并将返回值赋给idDraw变量。
        1. TQRichTextEmojiRun的drawRunWithRect方法中绘制了表情图片,返回YES。
        2. TQRichTextURLRun的drawRunWithRect方法返回NO。
        3. TQRichTextBaseRun的drawRunWithRect方法返回NO。
      5. 判断run的isResponseTouch属性,查找后发现是TQRichTextBaseRun的属性,注释说是是否响应触摸
      6. 如果响应触摸
        1. 在richTextRunRectDic中添加键值对,key为rect,value为run。
    8. 设置循环的条件,释放CF类型的变量。CF类型的变量不支持ARC。
  5. 继续寻找TQRichTextView中重载系统的方法。返现touchesBegan和touchesEnd方法。阅读这两个方法。
    1. 拿到触摸的点,转换点的坐标为以左下角为原点是的坐标。
    2. 判断是否能只想delegate
    3. 遍历richTextRunRectDic,判断点是否在rect中。如果在则调用delegate方法。
  6. 主要功能分析完毕。
  7. 通读TQRichTextBaseRun,了解其它属性和一些属性的默认值。
  8. 至此,整个TQRichTextViewDemo基本分析完毕。可得出如下结论。
    1. 类的结构关系

      1. TQRichTextEmojiRun—> TQRichTextImageRun—> TQRichTextBaseRun。
      2. TQRichTextURLRun—> TQRichTextBaseRun。
      3. TQRichTextEmojiRun和TQRichTextURLRun分别实现了analyzeText:runsArray方法用来从字符串中取出所需要的文字,并创建TQRichTextBaseRun对象添加进数组保存。
      4. TQRichTextEmojiRun和TQRichTextURLRun分别重载了replaceTextWithAttributedString方法来在字符串的特定range处添加Attributed属性。
      5. TQRichTextEmojiRun和TQRichTextURLRun分别重载了drawRunWithRect方法实现了自定义位置,并返回是否绘制了内容。
    2. 流程
      1. 给TQRichTextView更改属性。
      2. 调用drawRect。
      3. 调用TQRichTextEmojiRun和TQRichTextURLRun的analyzeText:runsArray方法解析字符串。
      4. 创建NSAttributedString。
      5. 为NSAttributedString添加属性。
      6. 调用TQRichTextEmojiRun和TQRichTextURLRun的replaceTextWithAttributedString方法为字符串添加属性。
      7. CoreText绘制NSAttributedString。
      8. 调用TQRichTextEmojiRun和TQRichTextURLRun的drawRunWithRect方法实现自定义绘制。
      9. 保存run和rect进字典,从字典取值判断是否能点击。
    3. 有以上可以得出结论,若要更换表情和文字的解析规则,则只需去TQRichTextEmojiRun或TQRichTextURLRun修改analyzeText:runsArray,replaceTextWithAttributedString,drawRunWithRect三个方法。
      1. analyzeText:runsArray修改解析规则。
      2. replaceTextWithAttributedString修改添加属性。
      3. drawRunWithRect修改自定义绘制。
    4. 若要实现点击,只需设置isResponseTouch为YES并实现TQRichTextViewDelegate。
  9. 由以上阅读代码发现如下缺陷
    1. TQRichTextView的awakeFromNib方法没实现,如过使用xib拖拽,则无法为TQRichTextView添加默认属性。

      1. 解决方法,添加awakeFromNib方法,并在其中为默认属性赋值。
    2. sizeWithFont和boundingRectWithSize:options:attributes:context:方法计算时不会计算行间距,TQRichTextView的lineSpacing若不设置为0会导致两方法计算的高度比TQRichTextView实际显示需要的高度小。
      1. 解决方法,TQRichTextView的lineSpacing设置为0。
时间: 2024-11-12 14:03:51

iOS开发--从TQRichTextViewDemo中学会分析工程的相关文章

iOS 开发 关于应用中使用拨打电话那点事

一.利用openURL(tel) 特点: 直接拨打, 不弹出提示. 并且, 拨打完以后, 留在通讯录中, 不返回到原来的应用. - (void)callPhone:(NSString *)phoneNumber {     //phoneNumber = "18369......"     NSMutableString * str=[[NSMutableString alloc] initWithFormat:@"tel:%@",phoneNumber];    

iOS开发遇到的坑之五--解决工程已存在plist表,数据却不能存入的问题

想写这篇博客其实在一两个月前开发遇见的时候就想把这个问题写成博客的,奈何自己一直懒外加一直没有时间,就把这个事情给耽搁了,好在当时知道下自己一定要把这个问题给描述出来,免得以后其他人遇到这个问题会纠结很久(其实就是我啦,基础知识不过关),所以当时就把这个过程给记录下来了 给这篇博客命名的时候,是不知道该怎么取名字的(语文不好),因为实在难以描述清楚,于是把它归为了 iOS开发遇到的坑系列文章(如果各位看官认为这确实是我基础的问题,请告诉欧文,我会修改过来的,顺便也学习学习) 大概就是下面这种情况

iOS开发实战——CollectionView中cell的间距设置

我在前面多篇博客中详细讲解了CollectionView的使用与自定义CollectionViewCell的设计,可以参考<iOS开发实战--CollectionView点击事件与键盘隐藏结合案例><iOS高级开发--CollectionView修改cell的文本及模型重构>这几篇博客.但是今天还是需要来讲讲CollectionView实现中的一个小小的坑,这是我最近在网上浏览时发现很多开发者经常犯的错,所以我觉得有必要来好好谈一谈. 一个CollectionView控件中,两个c

iOS开发:Objective-C中通知与协议的区别?

what is difference between NSNotification and protocol? (通知和协议的不同之处?) 我想大家都知道这个东西怎么用,但是更深层次的思考可能就比较少了吧,众所周知就是代理是一对一的,但是通知是可以多对多的.但是为什么是这个样子,有没有更深的思考过这个问题? 今天看了下网上的几个视频教程,KVO.KVC.谓词.通知,算是开发中的高级点的东西了.通知和协议都是类似于回调一样,于是就在思考通知和协议到底有什么不同,或者说什么时候该用通知,什么时候该用

iOS dateformatter设置GMT格式时间--iOS开发系列---项目中成长的知识四

今天在项目中开始接手客户端的签名这个模块,签名这个会在项目结束过后再单独写一下自己的心得! 今天讲讲在签名的过程中我们需要向服务器传送一个Date值,格式要求是格林威治时间,也就是GMT时间! 格式要求是 22 Oct 2015 10:20:35 GMT 而我通过[NSDate Date]获取到的格式是2015-10-22 10:20:35 所以就上网搜寻答案 说的是通过[NSDate Date]这个获取到的时间就是GMT地区的,所以我就觉得是转格式的地方出问题了 但是经过一番尝试后,无论是转格

把所有界面的状态栏字体颜色设置为白色--iOS开发系列---项目中成长的知识一

第一步: 在info.plist中 View controller-based status bar appearance这个属性设置为 View controller-based status bar appearance =NO 这个意思是:View Controller 不对status Bar 显示进行操作 第二步: 由于项目是一个以tabbarController的父类进行开发的;所以在这个父类里面添加如下方法: [[UIApplication sharedApplication] s

IOS开发info.plist中键值的含义

1.     Application does not run in background(鍵名:UIApplicationExistsOnSuspend)自從iOS4.0之後,當你在應用程式執行的時候按下Home鍵,應用程式並不會中斷目前的執行,而是躲到背景去了.因此希望使用者在按下Home鍵之後就要中斷目前程式的執行,請勾選這個選項. 2.     Application requires iPhone environment(鍵名:LSRequiresIPhoneOS)iOS的家族繫ㄌㄧ誒

IOS开发笔记_AFN中多线程依赖

我们平常在开发当中很可能会遇到同时开启两个网络请求,然后把资源下载下来后进行合并操作,那么在AFN中我们究竟要怎么做呢,当然,以下可能写出一些个人的封装技巧,有兴趣的朋友可以发继续关注我. #pragma mark -  getter - (NSOperationQueue *)queue { if (!_queue) { _queue = [[NSOperationQueuealloc]init]; } return_queue; } 这里是我个人对AFN的一个封装类,后面会说到 NSOper

IOS开发笔记之十七——一个关于工程目录读写权限的问题

1.从互联网上或者其他途径拿过来的工程代码,往往会报下面的提示: (1)打开文件的时候出现窗口提示You don’t have permission to save the file “project.xcworkspace” in the folder “****.xcodeproj”. (2)进入Xcode时出现窗口提示Could not add write permission to the file because you do not own it. Try modifying the