上手實踐 Text Kit 的應用
前兩天參加 iDev 大會的間隙,嘗試著寫了一個“字裡行間”的 Demo,用 Collection View 將基本 UI 介面搭好後,其中的難點便是寫文章時對於富文字的編輯了。
那麼,據說 Text Kit,這個蘋果在釋出 iOS 7 時帶給開發者的利器可以實現這一點。
以下,便是我們要實現的效果了。或者也可以下載“字裡行間”體驗一下,相信你會喜歡上它的。
先來看看 Text Kit 的架構
NSTextStorage
繼承自
NSAttributedString
,用來儲存並管理字串
以及文字屬性
,並在這些資訊發生修改時通知NSLayoutManager
。可以理解為觀察者模式中,被觀察的 Model。NSLayoutManager
中間元件,負責監聽
NSTextStorage
文字屬性修改發出的通知,並應用 Core Text 啟動佈局程式,並向NSTextContainer
獲取可用空間進行填充渲染。可以理解為 MVC 中的 Controller
NSTextContainer
控制文字在
UITextView
中文字的可繪製區域。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()
}
}複製程式碼
在控制器中建立使用自定義
NSTextStorage
的UITextView
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)