简介:
iOS7 的发布给开发者的案头带来了很多新工具。其中一个就是 TextKit(文本工具箱)。TextKit 由许多新的 UIKit 类组成,顾名思义,这些类就是用来处理文本的。
1.NSTextStorage 专门用于存储内容的
2.NSLayoutManager 专门用于管理布局
3.NSTextContainer 专门用于指定绘制的区域
字符串(String):要绘制文本,那么必然在某个地方有个字符串存储它。在默认的结构中,NSTextStorage 保存并管理这个字符串,在这种情况中,它可以远离绘制。但并不一定非得这样。使用 TextKit 时,文本可以来自任何适合的来源。例如,对于一个代码编辑器,字符串可以是一棵包含所有显示的代码的结构信息的注释语法树(annotated syntax tree, AST)。使用一个定制的文本存储,这个文本只在后面动态地添加字体或颜色高亮等文本属性装饰。这是第一次,开发者可以直接为文本组件使用自己的模型。只需要一个特别设计的文本存储。即:
NSTextStorage:如果你把文本系统看做一个模型-视图-控制器(MVC)架构,这个类代表的是模型。文本存储是中心对象,它知道所有的文本和属性信息。它只提供了两个存取器方法存取它们,并提供了另外两个方法来修改它们。后面我们将进一步了解它们。现在重要的是你得理解 NSTextStorage 是从它的父类 NSAttributedString 继承了这些方法。这就很清楚了,文本存储——从文本系统看来——仅仅是一个带有属性的字符串,以及几个扩展。这两者唯一的重大不同点是文本存储包含了一个方法来发送内容改变的通知。我们会马上介绍这部分内容。
UITextView:堆栈的另一头是实际的视图。在 TextKit 中,文本视图有两个目的:第一,它是文本系统用来绘制的视图。文本视图它自己并不会做任何绘制;它仅仅提供一个供其它类绘制的区域。作为视图层级机构中唯一的组件,第二个目的是处理所有的用户交互。具体来说,文本视图实现 UITextInput 的协议来处理键盘事件,它为用户提供了一种途径来设置一个插入点或选择文本。它并不对文本做任何实际上的改变,仅仅将这些改变请求转发给刚刚讨论的文本存储。
NSTextContainer:每个文本视图定义了一个文本可以绘制的区域。为此,每个文本视图都有一个文本容器,它精确地描述了这个可用的区域。在简单的情况下,这是一个垂直的无限相当大的矩形区域。文本被填充到这个区域,并且文本视图允许用户滚动它。然而,在更高级的情况下,这个区域可能是一个无限大的矩形。例如,当渲染一本书时,每一页都有最大的高度和宽度。文本容器会定义这个大小,并且不接受任何超出的文本。相同情况下,一幅图像可能占据了页面的一部分,文本应该沿着它的边缘重新排版。这也是由文本容器来处理的,我们会在后面的例子中看到这一点。
NSLayoutManager:布局管理器是中心组件,它把所有组件粘合在一起:
- 1、这个管理器监听文本存储中文本或属性改变的通知,一旦接收到通知就触发布局进程。
- 2、从文本存储提供的文本开始,它将所有的字符翻译为字形(Glyph)(附注2).
- 3、一旦字形全部生成,这个管理器向它的文本容器(们)查询文本可用以绘制的区域
- 4、然后这些区域被行逐步填充,而行又被字形逐步填充。一旦一行填充完毕,下一行开始填充。
- 5、对于每一行,布局管理器必须考虑断行行为(放不下的单词必须移到下一行)、连字符、内联的图像附件等等。
- 6、当布局完成,文本的当前显示状态被设为无效,然后文本管理器将前面几步排版好的文本设给文本视图。
CoreText:没有直接包含在 TextKit 中,CoreText 是进行实际排版的库。对于布局管理器的每一步,CoreText 被这样或那样的方式调用。它提供了从字符到字形的翻译,用它们来填充行,以及建议断字点。
使用示例:
customLabel.text = "百度一下你就知道 http://www.baidu.com"
代码示例:
import UIKit class JQLabel: UILabel { override var text: String?
{
didSet{
// 1.修改textStorage存储的内容
textStorage.setAttributedString(NSAttributedString(string: text!)) // 2.设置textStorage的属性
textStorage.addAttribute(NSFontAttributeName, value: UIFont.systemFontOfSize(), range: NSMakeRange(, text!.characters.count)) // 3.处理URL
self.URLRegex() // 2.通知layoutManager重新布局
setNeedsDisplay()
}
} // 如果是UILabel调用setNeedsDisplay方法, 系统会促发drawTextInRect
override func drawTextInRect(rect: CGRect) {
// 重绘
// 字形 ; 理解为一个小的UIView
/*
第一个参数: 指定绘制的范围
第二个参数: 指定从什么位置开始绘制
*/
layoutManager.drawGlyphsForGlyphRange(NSMakeRange(, text!.characters.count), atPoint: CGPointZero)
} override init(frame: CGRect) {
super.init(frame: frame)
setupSystem()
} required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setupSystem()
} private func setupSystem()
{
// 1.将layoutManager添加到textStorage
textStorage.addLayoutManager(layoutManager)
// 2.将textContainer添加到layoutManager
layoutManager.addTextContainer(textContainer)
} override func layoutSubviews() {
super.layoutSubviews() // 3.指定区域
textContainer.size = bounds.size
} // MARK: -懒加载
/*
只要textStorage中的内容发生变化, 就可以通知layoutManager重新布局
layoutManager重新布局需要知道绘制到什么地方, 所以layoutManager就会文textContainer绘制的区域
*/ // 专门用于存储内容的
// textStorage 中有 layoutManager
private lazy var textStorage = NSTextStorage() // 专门用于管理布局
// layoutManager 中有 textContainer
private lazy var layoutManager = NSLayoutManager() // 专门用于指定绘制的区域
private lazy var textContainer = NSTextContainer() func URLRegex()
{ // 1.创建一个正则表达式对象
do{
let dataDetector = try NSDataDetector(types: NSTextCheckingTypes(NSTextCheckingType.Link.rawValue)) let res = dataDetector.matchesInString(textStorage.string, options: NSMatchingOptions(rawValue: ), range: NSMakeRange(, textStorage.string.characters.count)) // 4取出结果
for checkingRes in res
{
let str = (textStorage.string as NSString).substringWithRange(checkingRes.range)
let tempStr = NSMutableAttributedString(string: str) // tempStr.addAttribute(NSForegroundColorAttributeName, value: UIColor.redColor(), range: NSMakeRange(0, str.characters.count))
tempStr.addAttributes([NSFontAttributeName: UIFont.systemFontOfSize(), NSForegroundColorAttributeName: UIColor.redColor()], range: NSMakeRange(, str.characters.count)) textStorage.replaceCharactersInRange(checkingRes.range, withAttributedString: tempStr)
}
}catch
{
print(error)
} } }
点击事件的处理:
import UIKit class JQTextView: UITextView { override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) { // 1.获取手指点击的位置
let touch = (touches as NSSet).anyObject()!
let point = touch.locationInView(touch.view) print(point) // 2.获取URL的区域
// 注意: 没有办法直接设置UITextRange的范围
let range = NSMakeRange(, )
// 只要设置selectedRange, 那么就相当于设置了selectedTextRange
selectedRange = range // 给定指定的range, 返回range对应的字符串的rect
// 返回数组的原因是因为文字可能换行
let array = selectionRectsForRange(selectedTextRange!) for selectionRect in array{
// let tempView = UIView(frame: selectionRect.rect)
// tempView.backgroundColor = UIColor.redColor()
// addSubview(tempView) if CGRectContainsPoint(selectionRect.rect, point)
{
print("点击了URL")
}
} }
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint point = [touch locationInView:touch.view];
CGPoint pp = [self convertPoint:point toView:self.textView];
GPLink *touchingLink = [self touchingLinkWithPoint:pp]; if (touchingLink) {
[[NSNotificationCenter defaultCenter] postNotificationName:GPLinkDidSelectedNotification object:nil userInfo:@{GPLinkText : touchingLink.text}];
} [self touchesCancelled:touches withEvent:event];
}
- (GPLink *)touchingLinkWithPoint:(CGPoint)point
{
__block GPLink *touchingLink = nil;
[self.links enumerateObjectsUsingBlock:^(GPLink *link, NSUInteger idx, BOOL *stop) {
for (UITextSelectionRect *selectionRect in link.rects) {
if (CGRectContainsPoint(selectionRect.rect, point)) {
NSLog(@"选中%@",NSStringFromCGRect(selectionRect.rect));
touchingLink = link;
break;
}
}
}];
return touchingLink;
}
第一篇、Swift_Textkit的基本使用的更多相关文章
-
从0开始搭建SQL Server AlwaysOn 第一篇(配置域控)
从0开始搭建SQL Server AlwaysOn 第一篇(配置域控) 第一篇http://www.cnblogs.com/lyhabc/p/4678330.html第二篇http://www.cnb ...
-
Python爬虫小白入门(四)PhatomJS+Selenium第一篇
一.前言 在上一篇博文中,我们的爬虫面临着一个问题,在爬取Unsplash网站的时候,由于网站是下拉刷新,并没有分页.所以不能够通过页码获取页面的url来分别发送网络请求.我也尝试了其他方式,比如下拉 ...
-
Three.js 第一篇:绘制一个静态的3D球体
第一篇就画一个球体吧 首先我们知道Three.js其实是一个3D的JS引擎,其中的强大之处就在于这个JS框架并不是依托于JQUERY来写的.那么,我们在写这一篇绘制3D球体的文章的时候,应该注意哪些地 ...
-
深入学习jQuery选择器系列第一篇——基础选择器和层级选择器
× 目录 [1]id选择器 [2]元素选择器 [3]类选择器[4]通配选择器[5]群组选择器[6]后代选择器[7]兄弟选择器 前面的话 选择器是jQuery的根基,在jQuery中,对事件处理.遍历D ...
-
【第一篇】ASP.NET MVC快速入门之数据库操作(MVC5+EF6)
目录 [第一篇]ASP.NET MVC快速入门之数据库操作(MVC5+EF6) [第二篇]ASP.NET MVC快速入门之数据注解(MVC5+EF6) [第三篇]ASP.NET MVC快速入门之安全策 ...
-
Android基础学习第一篇—Project目录结构
写在前面的话: 1. 最近在自学Android,也是边看书边写一些Demo,由于知识点越来越多,脑子越来越记不清楚,所以打算写成读书笔记,供以后查看,也算是把自己学到所理解的东西写出来,献丑,如有不对 ...
-
深入理解ajax系列第一篇——XHR对象
× 目录 [1]创建对象 [2]发送请求 [3]接收响应[4]异步处理[5]实例演示 前面的话 ajax是asynchronous javascript and XML的简写,中文翻译是异步的java ...
-
深入理解javascript对象系列第一篇——初识对象
× 目录 [1]定义 [2]创建 [3]组成[4]引用[5]方法 前面的话 javascript中的难点是函数.对象和继承,前面已经介绍过函数系列.从本系列开始介绍对象部分,本文是该系列的第一篇——初 ...
-
深入理解this机制系列第一篇——this的4种绑定规则
× 目录 [1]默认绑定 [2]隐式绑定 [3]隐式丢失[4]显式绑定[5]new绑定[6]严格模式 前面的话 如果要问javascript中哪两个知识点容易混淆,作用域查询和this机制绝对名列前茅 ...
-
前端工程师技能之photoshop巧用系列第一篇——准备篇
× 目录 [1]作用 [2]初始化 [3]常用工具[4]快捷键 前面的话 photoshop是前端工程师无法回避的一个软件,这个软件本身很强大,但我们仅仅需要通过这个工具来完成基本的切图工作即可.本文 ...
随机推荐
-
bzoj1491 社交网络
Description 在社交网络(socialnetwork)的研究中,我们常常使用图论概念去解释一些社会现象.不妨看这样的一个问题. 在一个社交圈子里有n个人,人与人之间有不同程度的关系.我们将这 ...
-
SQLServer2008R2 mdf文件还原数据库
偶然遇到要用mdf文件restore数据库,试了2个小时才弄出来,百度查出来的我试了都不太好用,或者是我没理解. 下面把我用的记录一下,以防忘记. 工具:SQLServer 2008R2 步骤: 1. ...
-
Java缓存学习之二:浏览器缓存机制
浏览器端的九种缓存机制介绍 浏览器缓存是浏览器端保存数据用于快速读取或避免重复资源请求的优化机制,有效的缓存使用可以避免重复的网络请求和浏览器快速地读取本地数据,整体上加速网页展示给用户.浏览器端缓存 ...
-
[转]Blocking Code Injection on iOS and OS X
Source:http://www.samdmarshall.com/blog/blocking_code_injection_on_ios_and_os_x.html Yesterday I pos ...
-
avd
http://*.com/questions/2662650/making-the-android-emulator-run-faster http://www.cnblogs ...
-
JStorm与Storm源码分析(四)--均衡调度器,EvenScheduler
EvenScheduler同DefaultScheduler一样,同样实现了IScheduler接口, 由下面代码可以看出: (ns backtype.storm.scheduler.EvenSche ...
-
subset_lat_dir.sh
#!/bin/bash # Copyright 2018 Jarvan Wang # Copyright 2017 Vimal Manohar # Apache 2.0. cmd=ru ...
-
C# VideoAPI
using System; using System.Runtime.InteropServices; public class VideoAPI //视频API类 { // 视频API调用 [Dll ...
-
Ajax发送POST请求对数据的封装
Ajax发送POST请求把数据到后端后,后端收到数据并解析出来 示列一: Ajax发送请求,这里主要是发送一个数组的数据类型到后端,如果没有先把数组进行格式化成字符串的话,后端就收了就是一个字符串类型 ...
-
Sping实战之通过JAVA代码装配Bean
尽管在很多场景下通过组件扫描和自动装配实现Spring的自动化配置是更为推荐的方式,但有时候自动化配置的方案行不通,因此需要明确配置Spring.比如说,你想要将第三方库中的组件装配到你的应用中,在这 ...