使用 TextKit 實現語法高亮文本編輯器

jopen 8年前發布 | 14K 次閱讀 TextKit iOS開發 移動開發

語法高亮對于我們開發者來說是幾乎離不開的功能,它能夠幫助我們非常清晰的展現出文本,提高可讀性。TextKit 的出現讓語法高亮這類的功能實現起來變得特別簡單,我們就來看看如何使用 TextKit 來實現我們自己的語法高亮 App 吧。

語法高亮簡介

語法高亮功能用起來簡單,但實現原理并不容易。會涉及到很多方面的知識 - 比如確定底層文本的存儲結構,如何將文本的結構與 UI 進行關聯。 往往實現一個完整的語法高亮功能,即使是有經驗的開發者,也要花去很長時間。

不過好消息是 TextKit 的出現,讓這一切變得簡單起來。 它提供了一個叫做 NSTextStorage 的類,可以實現高亮功能,這是它的結構:

這樣看大家可能會覺得比較抽象,那么讓我帶大家把它具體化。我們先撇開這個結構,來看看 UITextView 的一個構造方法:

init(frame frame: CGRect,textContainer textContainer: NSTextContainer?)

UITextView 除了我們常見的構造方法外,還提供了一個這樣的構造方法,它接受一個 NSTextContainer 類型的參數。 再看看我們上面那張圖中的 NSTextContainer, 稍微建立起一點關系了。

簡單來說是這樣,如果以 MVC 的思路來思考的話, NSTextStorage 相當于模型層,用于管理文本的底層存儲,以及如何定義文本顯示的樣式。 高亮顯示功能的主要代碼都是在這個類中完成的。

NSLayoutManager 相當與控制器層,它負責把 NSTextStorage 的文本內容繪制到相應的視圖上,并且它還負責文字的排版處理等。

NSTextContainer 定義了文本在 UITextView 上面的顯示區域。

因為這些分層的概念,我們甚至可以把同一個 NSTextStorage 同時顯示到兩個不同的視圖上面,這個內容我們這里不多做探討。

NSTextStorage

現在咱們開始進入正題,首先需要實現一個繼承自 NSTextStorage 的類:

class SwiftTextStorage: NSTextStorage {

  var _string = NSMutableAttributedString()

}

定義了一個 NSMutableAttributedString 類型的屬性,它用于我們的底層存儲。然后需要實現幾個約定的 getter 和 setter 方法:

class SwiftTextStorage: NSTextStorage {

    override var string:String {

        get {

            return _string.string

        }

    }

    override func attributesAtIndex(location: Int, effectiveRange range: NSRangePointer) -> [String : AnyObject] {

        return _string.attributesAtIndex(location, effectiveRange: range)

    }    

}

對這兩個 getter 的實現也很簡單,只是對 _string 對象相應方法的一個包裝,然后我們在實現以下 setter 方法:

override func replaceCharactersInRange(range: NSRange, withString str: String) {

    _string.replaceCharactersInRange(range, withString: str)
    self.edited(NSTextStorageEditActions.EditedCharacters, range: range, changeInLength: str.characters.count - range.length)

}

override func setAttributes(attrs: [String : AnyObject]?, range: NSRange) {

    _string.setAttributes(attrs, range: range)
    self.edited(NSTextStorageEditActions.EditedAttributes, range: range, changeInLength: 0)

}

這兩個 setter 方法也是對 _string 的一個包裝。

接下來,我們實現最關鍵的方法 processEditing , 這個方法會在 UITextView 的文本被更改的時候被調用,我們可以在這里匹配出要進行高亮的關鍵字:

override func processEditing() {

    super.processEditing()

    do {

        let regex = try NSRegularExpression(pattern: "Swift", options: [])

        let paragraphRange = (self.string as NSString).paragraphRangeForRange(self.editedRange)

        regex.enumerateMatchesInString(self.string, options: [], range: paragraphRange) { result, flags, stop in

            self.addAttribute(NSForegroundColorAttributeName, value: UIColor.redColor(), range: result!.range)

        }

    } catch {

    }

}

我們這里聲明了一個正則表達式,匹配字符串 Swift,然后使用正則表達式進行匹配,將匹配到的字符串改變為紅色:

self.addAttribute(NSForegroundColorAttributeName, value: UIColor.redColor(), range: result!.range)

這個用到了 NSAttributedString 的特性。讓我們很容易的位 UITextView 中的文本設置屬性。

設置 UITextView

我們剛才完成了 NSTextStorage 子類的實現,現在我們就可以用它來構建 UITextView 了:

class ViewController: UIViewController {

    var textView:UITextView?    
    var textStorage = SwiftTextStorage()

    override func viewDidLoad() {
        super.viewDidLoad()

        let layoutManager = NSLayoutManager()

        textStorage.addLayoutManager(layoutManager)

        let textContainer = NSTextContainer()
        layoutManager.addTextContainer(textContainer)

        self.textView = UITextView(frame: CGRectMake(0, 20, self.view.frame.size.width, self.view.frame.size.height), textContainer: textContainer)
        self.textView?.text = "最近各大網站上最喜大普奔的新聞莫過于 Swift 正式開源這條了。這無疑是一個里程碑式的前進,蘋果也向開源社區更加進了一步。那么 Swift 開源后有什么具體的改變呢,我們一一道來。"
        self.view.addSubview(self.textView!)

    }

}

我們這里建立了 UITextView 的層級結構,首先使用 textStorage.addLayoutManager(layoutManager) 將 textStoragelayoutManager 關聯起來。 然后用 layoutManager.addTextContainer(textContainer) 將 layoutManager 和 textContainer 關聯起來。

最后使用 UITextView 的構造方法將 textContainer 實例關聯起來。這樣我們整體的 TextKit 結構就創建完成了。

現在我們可以運行程序,看到效果了:

總結

TextKit為我們提供了很不錯的文本處理接口,這些特性在 iOS 7 之后才提供出來。 主要是因為性能的原因, 文本繪制在我們看來是最基本的功能,但它的底層實現并不那么簡單。 隨著 iPhone 硬件的升級,以及 iOS 系統的優化, TextKit 也隨之更加強大。

更詳細關于 TextKit 的內容,大家可以參看蘋果官方的文檔,進行更深入的研究。

本篇文章的示例代碼大家可以在 Github 主頁上面下載: https://github.com/swiftcafex/syntaxHighlightingSamples

來自: http://www.swiftcafe.io/2016/01/14/syntax-highlighting/

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!