NSAttributedString 在文本和背景之前和之后具有填充

Wal*_*ter 9 padding nsattributedstring uilabel ios swift

我想使用 来设置文本样式NSAttributedString。文本应该有背景和自定义填充,以便文本到背景边缘有一点空间。

这就是我想要实现的目标:

我想要实现什么

这是我不想实现的目标(第二行的背景不是特定于单词/字符的):

我不想实现什么

这是我在操场上尝试过的代码:

let quote = "some text with a lot of other text and \nsome other text."
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.alignment = .left
let attributes: [NSAttributedString.Key: Any] = [
    NSAttributedString.Key.paragraphStyle: paragraphStyle,
    NSAttributedString.Key.backgroundColor: UIColor.red,
    NSAttributedString.Key.foregroundColor: UIColor.white
]
let attributedQuote = NSAttributedString(string: quote, attributes: attributes)
Run Code Online (Sandbox Code Playgroud)

这就是游乐场预览呈现的内容:

[![操场预览][3]][3]

文本非常接近背景边缘。有什么办法可以让文本的背景有一些空间给文本吗?我需要一些填充物。我尝试使用headIndent,但这会将文本及其背景移动到右侧,而不仅仅是文本。因此它对于填充没有用处。

XLE*_*_22 9

\n

文本应该有背景和自定义填充,以便文本到背景边缘有一点空间。

\n
\n\n

我发现的最好的方法是使用 TextKit,它有点麻烦,但它是完全模块化的,并且是为此目的而设计的。
\n在我看来,在其方法中绘制矩形并不取决于 TextView 本身draw,而是 LayoutManager 的工作。

\n\n

下面提供了项目中使用的整个类,以便简化复制粘贴工作(Swift 5.1 - iOS 13)

\n\n

AppDelegate.swift存储属性以在应用程序中的任何位置获取文本。

\n\n
class AppDelegate: UIResponder, UIApplicationDelegate {\n\n    lazy var TextToBeRead: NSAttributedString = {\n\n        var text: String\n        if let filepath = Bundle.main.path(forResource: "TextToBeRead", ofType: "txt") {\n            do { text = try String(contentsOfFile: filepath) }\n            catch { text = "E.R.R.O.R." }\n        } else { text = "N.O.T.H.I.N.G." }\n\n        return NSAttributedString(string: text)\n    }()\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

ViewController.swift \xe2\x9f\xb9 全屏时只有一个文本视图。

\n\n
class ViewController: UIViewController, NSLayoutManagerDelegate {\n\n    @IBOutlet weak var myTextView: UITextView!\n    let textStorage = MyTextStorage()\n    let layoutManager = MyLayoutManager()\n\n    override func viewDidLoad() {\n        super.viewDidLoad()\n\n        self.layoutManager.delegate = self\n\n        self.textStorage.addLayoutManager(self.layoutManager)\n        self.layoutManager.addTextContainer(myTextView.textContainer)\n\n        let appDelegate = UIApplication.shared.delegate as? AppDelegate\n        self.textStorage.replaceCharacters(in: NSRange(location: 0, length: 0),\n                                           with: (appDelegate?.TextToBeRead.string)!)\n    }\n\n    func layoutManager(_ layoutManager: NSLayoutManager,\n                       lineSpacingAfterGlyphAt glyphIndex: Int,\n                       withProposedLineFragmentRect rect: CGRect) -> CGFloat { return 20.0 }\n\n    func layoutManager(_ layoutManager: NSLayoutManager,\n                       paragraphSpacingAfterGlyphAt glyphIndex: Int,\n                       withProposedLineFragmentRect rect: CGRect) -> CGFloat { return 30.0 }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

MyTextStorage.swift

\n\n
class MyTextStorage: NSTextStorage {\n\n    var backingStorage: NSMutableAttributedString\n\n    override init() {\n\n        backingStorage = NSMutableAttributedString()\n        super.init()\n    }\n\n    required init?(coder: NSCoder) {\n\n        backingStorage = NSMutableAttributedString()\n        super.init(coder: coder)\n    }\n\n//    Overriden GETTERS\n    override var string: String {\n        get { return self.backingStorage.string }\n    }\n\n    override func attributes(at location: Int,\n                             effectiveRange range: NSRangePointer?) -> [NSAttributedString.Key : Any] {\n\n        return backingStorage.attributes(at: location, effectiveRange: range)\n    }\n\n//    Overriden SETTERS\n    override func replaceCharacters(in range: NSRange, with str: String) {\n\n        backingStorage.replaceCharacters(in: range, with: str)\n        self.edited(.editedCharacters,\n                    range: range,\n                    changeInLength: str.count - range.length)\n    }\n\n    override func setAttributes(_ attrs: [NSAttributedString.Key : Any]?, range: NSRange) {\n\n        backingStorage.setAttributes(attrs, range: range)\n        self.edited(.editedAttributes,\n                    range: range,\n                    changeInLength: 0)\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

MyLayoutManager.swift

\n\n
import CoreGraphics //Important to draw the rectangles\n\nclass MyLayoutManager: NSLayoutManager {\n\n    override init() { super.init() }\n\n    required init?(coder: NSCoder) { super.init(coder: coder) }\n\n    override func drawBackground(forGlyphRange glyphsToShow: NSRange, at origin: CGPoint) {   \n        super.drawBackground(forGlyphRange: glyphsToShow, at: origin)\n\n        self.enumerateLineFragments(forGlyphRange: glyphsToShow) { (rect, usedRect, textContainer, glyphRange, stop) in\n\n            var lineRect = usedRect\n            lineRect.size.height = 30.0\n\n            let currentContext = UIGraphicsGetCurrentContext()\n            currentContext?.saveGState()\n\n            currentContext?.setStrokeColor(UIColor.red.cgColor)\n            currentContext?.setLineWidth(1.0)\n            currentContext?.stroke(lineRect)\n\n            currentContext?.restoreGState()\n        }\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

...这就是最终的样子:\n在此输入图像描述

\n\n

只需自定义颜色并调整一些参数以适应您的项目,但这就是显示NSAttributedString 的基本原理,该字符串在文本和背景之前和之后具有填充......如果需要的话,可以显示超过 2 行。

\n