日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

BSText - YY大神的富文本框架 YYText 的 Swift 版本

發布時間:2023/12/10 编程问答 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 BSText - YY大神的富文本框架 YYText 的 Swift 版本 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

原文鏈接

前言

??度過春節期間的安逸期,需要從慵懶的狀態盡快恢復過來,節前有幾個月時間,趁著公司業務線不怎么繁忙,抱著學習的態度,嘗試將 YY 大神的 YYText 用 Swift 重新實現一下(之前用 Swift 最多寫寫 Demo,沒有用來做項目)。但是由于年前還有個 bug 沒有修復,怕大家的 issue 雪花般飛過來,果斷沒敢開源出來。新年新氣象,改完 bug 趕緊和大家分享一下啦 ?

??目前項目已經實現了 YYText 中的所有功能,如果大家遇到什么問題,歡迎提 issue,或者郵箱聯系 a1049145827@hotmail.com ,如果覺得有用,請不要吝惜您的 star ?。

??用過 YYText 的同學應該已經相當熟悉了,傳送門。

一些挑戰

??由于近年來 Swift 發展迅速,ABI 一直不能穩定下來,導致開發者們都在吐槽:“每年學習一門新語言”,這樣就直接導致在網上查資料非常困難,好不容易查到的資料,demo 代碼甚至都不能通過編譯,這樣推進的效率大打折扣,確實非常痛苦,眼看 Swift 就要發布 Swift5,心里似乎又有了希望。于是決心要用 Swift 把 YYText 的功能實現一遍,一來練習 Swift 語法,二來以后也可以持續維護,希望好的輪子可以被更多的開發者認可和采用,目前本項目已經可以在 Swift5 (Xcode10 beta3)環境下正常編譯運行。

項目介紹

功能強大的 iOS 富文本編輯與顯示框架。
(該項目是 YYText 的 Swift 版本,項目的前綴 'BS' 來自于 BlueSky,就是創作了《冰河世紀》系列電影的 BlueSky 工作室)

特性

  • API 兼容 UILabel 和 UITextView
  • 支持高性能的異步排版和渲染
  • 擴展了 CoreText 的屬性以支持更多文字效果
  • 支持 UIImage、UIView、CALayer 作為圖文混排元素
  • 支持添加自定義樣式的、可點擊的文本高亮范圍
  • 支持自定義文本解析 (內置簡單的 Markdown/表情解析)
  • 支持文本容器路徑、內部留空路徑的控制
  • 支持文字豎排版,可用于編輯和顯示中日韓文本
  • 支持圖片和富文本的復制粘貼
  • 文本編輯時,支持富文本占位符
  • 支持自定義鍵盤視圖
  • 撤銷和重做次數的控制
  • 富文本的序列化與反序列化支持
  • 支持多語言,支持 VoiceOver
  • 全部代碼都有文檔注釋

架構

本項目架構與 YYText 保持一致

文本屬性

BSText 原生支持的屬性

DemoAttribute NameClass
TextAttachmentTextAttachment
TextHighlightTextHighlight
TextBindingTextBinding
TextShadow
TextInnerShadow
TextShadow
TextBorderTextBorder
TextBackgroundBorderTextBorder
TextBlockBorderTextBorder
TextGlyphTransform NSValue(CGAffineTransform)
TextUnderlineTextDecoration
TextStrickthroughTextDecoration
TextBackedStringTextBackedString

BSText 支持的 CoreText 屬性

DemoAttribute NameClass
Font UIFont(CTFontRef)
Kern NSNumber
StrokeWidth NSNumber
StrokeColor CGColorRef
Shadow NSShadow
Ligature NSNumber
VerticalGlyphForm NSNumber(BOOL)
WritingDirection NSArray(NSNumber)
RunDelegate CTRunDelegateRef
TextAlignment NSParagraphStyle
(NSTextAlignment)
LineBreakMode NSParagraphStyle
(NSLineBreakMode)
LineSpacing NSParagraphStyle
(CGFloat)
ParagraphSpacing
ParagraphSpacingBefore
NSParagraphStyle
(CGFloat)
FirstLineHeadIndent NSParagraphStyle
(CGFloat)
HeadIndent NSParagraphStyle
(CGFloat)
TailIndent NSParagraphStyle
(CGFloat)
MinimumLineHeight NSParagraphStyle
(CGFloat)
MaximumLineHeight NSParagraphStyle
(CGFloat)
LineHeightMultiple NSParagraphStyle
(CGFloat)
BaseWritingDirection NSParagraphStyle
(NSWritingDirection)
DefaultTabInterval
TabStops
NSParagraphStyle
CGFloat/NSArray(NSTextTab)

用法

基本用法

// BSLabel (和 UILabel 用法一致) let label = BSLabel() label.frame = ... label.font = ... label.textColor = ... label.textAlignment = ... label.lineBreakMode = ... label.numberOfLines = ... label.text = ...// BSTextView (和 UITextView 用法一致) let textView = BSTextView() textView.frame = ... textView.font = ... textView.textColor = ... textView.dataDetectorTypes = ... textView.placeHolderText = ... textView.placeHolderTextColor = ... textView.delegate = ... 復制代碼

屬性文本

// 1. 創建一個屬性文本 let text = NSMutableAttributedString(string: "Some Text, blabla...")// 2. 為文本設置屬性 text.bs_font = UIFont.boldSystemFont(ofSize:30) text.bs_color = UIColor.blue text.bs_set(color: UIColor.red, range: NSRange(location: 0, length: 4)) text.bs_lineSpacing = 10// 3. 賦值到 BSLabel 或 BSTextView let label = BSLabel() label.frame = CGRect(x: 15, y: 100, width: 200, height: 80) label.attributedText = textlet textView = BSTextView() textView.frame = CGRect(x: 15, y: 200, width: 200, height: 80) textView.attributedText = text 復制代碼

文本高亮

你可以用一些已經封裝好的簡便方法來設置文本高亮:

text.bs_set(textHighlightRange: range,color: UIColor.blue,backgroundColor: UIColor.gray) { (view, text, range, rect) inprint("tap text range:...") } 復制代碼

或者用更復雜的辦法來調節文本高亮的細節:

// 1. 創建一個"高亮"屬性,當用戶點擊了高亮區域的文本時,"高亮"屬性會替換掉原本的屬性 let border = TextBorder.border(with: UIColor.gray, cornerRadius: 3)let highlight = TextHighlight() highlight.color = .white highlight.backgroundBorder = highlightBorder highlight.tapAction = { (containerView, text, range, rect) inprint("tap text range:...")// 你也可以把事件回調放到 BSLabel 和 BSTextView 來處理。 }// 2. 把"高亮"屬性設置到某個文本范圍 let attributedText = NSMutableAttributedString(string: " ") attributedText.bs_set(textHighlight: highlight, range: highlightRange)// 3. 把屬性文本設置到 BSLabel 或 BSTextView let label = BSLabel() label.attributedText = attributedTextlet textView = BSTextView() textView.delegate = self textView.attributedText = ...// 4. 接受事件回調 label.highlightTapAction = { (containerView, text, range, rect) inprint("tap text range:...") }; label.highlightLongPressAction = { (containerView, text, range, rect) inprint("tap text range:...") };// MARK: - TextViewDelegate func textView(_ textView: BSTextView, didTap highlight: TextHighlight, in characterRange: NSRange, rect: CGRect) {print("tap text range:...") } func textView(_ textView: BSTextView, didLongPress highlight: TextHighlight, in characterRange: NSRange, rect: CGRect) {print("tap text range:...") } 復制代碼

圖文混排

let text = NSMutableAttributedString() let font = UIFont.systemFont(ofSize: 16)// 嵌入 UIImage let image = UIImage.init(named: "dribbble64_imageio") guard let attachment = NSMutableAttributedString.bs_attachmentString(with: image, contentMode: .center, attachmentSize: image?.size ?? .zero, alignTo: font, alignment: .center) else {return } text.append(attachment)// 嵌入 UIView let switcher = UISwitch() switcher.sizeToFit() guard let attachment1 = NSMutableAttributedString.bs_attachmentString(with: switcher, contentMode: .center, attachmentSize: switcher.frame.size, alignTo: font, alignment: .center) else {return } text.append(attachment1)// 嵌入 CALayer let layer = CAShapeLayer() layer.path = ... guard let attachment2 = NSMutableAttributedString.bs_attachmentString(with: layer, contentMode: .center, attachmentSize: layer.frame.size, alignTo: font, alignment: .center) else {return } text.append(attachment2) 復制代碼

文本布局計算

let text = NSAttributedString() let size = CGSize(width: 100, height: CGFloat.greatestFiniteMagnitude) let container = TextContainer() container.size = size guard let layout = TextLayout(container: container, text: text) else {return }// 獲取文本顯示位置和大小 layout.textBoundingRect // get bounding rect layout.textBoundingSize // get bounding size// 查詢文本排版結果 layout.lineIndex(for: CGPoint(x: 10, y: 10)) layout.closestLineIndex(for: CGPoint(x: 10, y: 10)) layout.closestPosition(to: CGPoint(x: 10, y: 10)) layout.textRange(at: CGPoint(x: 10, y: 10)) layout.rect(for: TextRange(range: NSRange(location: 10, length: 2))) layout.selectionRects(for: TextRange(range: NSRange(location: 10, length: 2)))// 顯示文本排版結果 let label = BSLabel() label.frame = CGRect(x: 0, y: 0, width: layout.textBoundingSize.width, height: layout.textBoundingSize.height) label.textLayout = layout; 復制代碼

文本行位置調整

// 由于中文、英文、Emoji 等字體高度不一致,或者富文本中出現了不同字號的字體, // 可能會造成每行文字的高度不一致。這里可以添加一個修改器來實現固定行高,或者自定義文本行位置。// 簡單的方法: // 1. 創建一個文本行位置修改類,實現 `TextLinePositionModifier` 協議。 // 2. 設置到 Label 或 TextView。let modifier = TextLinePositionSimpleModifier() modifier.fixedLineHeight = 24let label = BSLabel() label.linePositionModifier = modifier// 完全控制: let modifier = TextLinePositionSimpleModifier() modifier.fixedLineHeight = 24let container = TextContainer() container.size = CGSize(width: 100, height: CGFloat.greatestFiniteMagnitude) container.linePositionModifier = modifierguard let layout = TextLayout(container: container, text: text) else {return } let label = BSLabel() label.size = layout.textBoundingSize label.textLayout = layout 復制代碼

異步排版和渲染

// 如果你在顯示字符串時有性能問題,可以這樣開啟異步模式: let label = BSLabel() label.displaysAsynchronously = true// 如果需要獲得最高的性能,你可以在后臺線程用 `TextLayout` 進行預排版: let label = BSLabel() label.displaysAsynchronously = true // 開啟異步繪制 label.ignoreCommonProperties = true // 忽略除了 textLayout 之外的其他屬性DispatchQueue.global().async {// 創建屬性字符串let text = NSMutableAttributedString(string: "Some Text")text.bs_font = UIFont.systemFont(ofSize: 16)text.bs_color = UIColor.graytext.bs_set(color: .red, range: NSRange(location: 0, length: 4))// 創建文本容器let container = TextContainer()container.size = CGSize(width: 100, height: CGFloat.greatestFiniteMagnitude);container.maximumNumberOfRows = 0;// 生成排版結果let layout = TextLayout(container: container, text: text)DispatchQueue.main.async {label.frame = CGRect(x: 0, y: 0, width: layout.textBoundingSize.width, height: layout.textBoundingSize.height)label.textLayout = layout;} } 復制代碼

文本容器控制

let label = BSLabel() label.textContainerPath = UIBezierPath(...) label.exclusionPaths = [UIBezierPath(), ...] label.textContainerInset = UIEdgeInsets(...) label.verticalForm = true/falselet textView = BSTextView() textView.exclusionPaths = [UIBezierPath(), ...] textView.textContainerInset = UIEdgeInsets(...) textView.verticalForm = true/false 復制代碼

文本解析

// 1. 創建一個解析器// 內置簡單的表情解析 let simpleEmoticonParser = TextSimpleEmoticonParser() var mapper = [String: UIImage]() mapper[":smile:"] = UIImage.init(named: "smile.png") mapper[":cool:"] = UIImage.init(named: "cool.png") mapper[":cry:"] = UIImage.init(named: "cry.png") mapper[":wink:"] = UIImage.init(named: "wink.png") simpleEmoticonParser.emoticonMapper = mapper;// 內置簡單的 markdown 解析 let markdownParser = TextSimpleMarkdownParser() markdownParser.setColorWithDarkTheme()// 實現 `TextParser` 協議的自定義解析器 let parser = MyCustomParser()// 2. 把解析器添加到 BSLabel 或 BSTextView let label = BSLabel() label.textParser = parserlet textView = BSTextView() textView.textParser = parser 復制代碼

Debug

// 設置一個全局的 debug option 來顯示排版結果。 let debugOption = TextDebugOption() debugOption.baselineColor = .red debugOption.ctFrameBorderColor = .red debugOption.ctLineFillColor = UIColor(red: 0, green: 0.463, blue: 1, alpha: 0.18) debugOption.cgGlyphBorderColor = UIColor(red: 1, green: 0.524, blue: 0, alpha: 0.2) TextDebugOption.setSharedDebugOption(debugOption) 復制代碼

更多示例

查看演示工程 Demo/BSTextDemo.xcodeproj:




安裝

CocoaPods

  • 在 Podfile 中添加 pod 'BSText'。

    source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' use_frameworks!target 'MyApp' do# your other pod# ...pod 'BSText', '~> 1.0' end 復制代碼
  • 執行 pod install 或 pod update。

  • 導入模塊 import BSText,OC 項目中使用 @import BSText;。

  • Carthage

  • 在 Cartfile 中添加 github "a1049145827/BSText"。
  • 執行 carthage update --platform ios 并將生成的 framework 添加到你的工程。
  • 導入模塊 import BSText,OC 項目中使用 @import BSText;。
  • 手動安裝

  • 下載 BSText 文件夾內的所有內容。
  • 將 BSText 內的源文件添加(拖放)到你的工程。
  • 鏈接以下 frameworks:
    • UIKit
    • CoreFoundation
    • CoreText
    • QuartzCore
    • Accelerate
    • MobileCoreServices
  • 導入模塊 import BSText,OC 項目中使用 @import BSText;。
  • 注意

    你可以添加 YYImage 或 YYWebImage 到你的工程,以支持動畫格式(GIF/APNG/WebP)的圖片。

    文檔

    本項目目前還沒有生成在線文檔,你可以在 CocoaDocs 查看 YYText 的在線 API 文檔,也可以用 appledoc 本地生成文檔。

    系統要求

    該項目最低支持 iOS 8.0 和 Xcode 10.0。

    已知問題

    • 與 YYText 一樣,BSText 并不能支持所有 CoreText/TextKit 的屬性,比如 NSBackgroundColor、NSStrikethrough、NSUnderline、NSAttachment、NSLink 等,但 BSText 中基本都有對應屬性作為替代。詳情見上方表格。
    • BSTextView 未實現局部刷新,所以在輸入和編輯大量的文本(比如超過大概五千個漢字、或大概一萬個英文字符)時會出現較明顯的卡頓現象。
    • 豎排版時,添加 exclusionPaths 在少數情況下可能會導致文本顯示空白。
    • 當添加了非矩形的 textContainerPath,并且有嵌入大于文本排版方向寬度的 RunDelegate 時,RunDelegate 之后的文字會無法顯示。這是 CoreText 的 Bug(或者說是 Feature)。

    轉載于:https://juejin.im/post/5c7c8a91f265da2dd53fe444

    總結

    以上是生活随笔為你收集整理的BSText - YY大神的富文本框架 YYText 的 Swift 版本的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。