iOS学习开发首页投稿(暂停使用,暂停投稿)

探究 Text Kit 和 Core Text 的前世今生 (T

2016-11-10  本文已影响311人  Binboy_

上手实践 Text Kit 的应用

字里行间 Demo

前两天参加 iDev 大会的间隙,尝试着写了一个“字里行间”的 Demo,用 Collection View 将基本 UI 界面搭好后,其中的难点便是写文章时对于富文本的编辑了。那么,据说 Text Kit,这个苹果在发布 iOS 7 时带给开发者的利器可以实现这一点。

以下,便是我们要实现的效果了。或者也可以下载“字里行间”体验一下,相信你会喜欢上它的。

编辑富文本

先来看看 Text Kit 的架构

继承自 NSAttributedString,用来保存并管理 字符串 以及 文本属性,并在这些信息发生修改时通知 NSLayoutManager。可以理解为观察者模式中,被观察的 Model。

中间组件,负责监听NSTextStorage文本属性修改发出的通知,并应用 Core Text 启动布局进程,并向 NSTextContainer 获取可用空间进行填充渲染。可以理解为 MVC 中的 Controller

控制文本在 UITextView 中文本的可绘制区域。

实现 UITextInput 协议,处理用户交互,并将文本修改信息转发给 NSTextStorage 进行实际的更改。

开始写代码吧

自定义一个继承 NSTextStorage 的类

class ZiInteractiveTextStorage: NSTextStorage {
    let backingStore = NSMutableAttributedString()
    
    override var string: String {
        return backingStore.string
    }
    
    override func attributes(at location: Int, effectiveRange range: NSRangePointer?) -> [String : Any] {
        return backingStore.attributes(at: location, effectiveRange: range)
    }
    
    override func replaceCharacters(in range: NSRange, with str: String) {
        print("replaceCharactersInRange\(range) withString:\(str)")
        
        beginEditing()
        backingStore.replaceCharacters(in: range, with: str)
        edited([.editedCharacters, .editedAttributes], range: range, changeInLength: (str as NSString).length - range.length)
        endEditing()
    }
    
    override func setAttributes(_ attrs: [String : Any]?, range: NSRange) {
        print("setAttributes:\(attrs) range:\(range)")
        
        beginEditing()
        backingStore.setAttributes(attrs, range: range)
        edited(.editedAttributes, range: range, changeInLength: 0)
        endEditing()
    }

    public func performReplacemetsFor(changedRange: NSRange) {
        let extendedRange = NSUnionRange(changedRange, NSString(string: backingStore.string).lineRange(for: NSMakeRange(NSMaxRange(changedRange), 0)))
        applyStyleTo(extendedRange) // 自定义富文本格式的修改规则
    }
    
    override func processEditing() {
        performReplacemetsFor(changedRange: editedRange)
        super.processEditing()
    }
}

在控制器中创建使用自定义 NSTextStorageUITextView

class EditCreationViewController: UIViewController {
    var textView: UITextView!
    var textStorage: ZiInteractiveTextStorage!

    func createTextView() {
        
        let attrString = template.content.utf8Data?.attributedString
        
        textStorage = ZiInteractiveTextStorage()
        textStorage.append(attrString!)
        
        let newTextViewRect = view.bounds
        
        let layoutManager = NSLayoutManager()
        
        let containerSize = CGSize(width: newTextViewRect.width, height: CGFloat.greatestFiniteMagnitude)
        let container = NSTextContainer(size: containerSize)
        container.widthTracksTextView = true
        layoutManager.addTextContainer(container)
        textStorage.addLayoutManager(layoutManager)
        
        textView = UITextView(frame: newTextViewRect, textContainer: container)
        textView.delegate = self
        view.addSubview(textView)
    }
}

这样,就实现了输入时按富文本格式的规则动态布局文本样式。

下一篇,我们再来看看 iOS 7.0 之前没有 Text Kit 时,是如何应用 Core Text 进行排版布局,也就是 Text Kit 中 NSLayoutManager 为我们做了些什么。

更多参考

Getting to Know TextKit (Objective-C)

Text Kit Tutorial: Getting Started (Swift)

上一篇下一篇

猜你喜欢

热点阅读