NSAttributedString,在文本和背景前后有填充 [英] NSAttributedString with padding before and after the text and a background
问题描述
我想使用NSAttributedString
为文本设置样式.文本应具有背景和自定义填充,以使文本与背景边缘之间有一些空间.
I want to style a text using NSAttributedString
. The text should have a background and a custom padding, so that the text has a little bit of space to the background's edge.
这是我要实现的目标:
这是我不想实现的(第二行的背景不是单词/字符专用的):
这是我在操场上尝试过的代码:
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)
这是游乐场预览呈现的内容:
[![运动场预览] [3]] [3]
[![playground preview][3]][3]
文本非常靠近背景边缘.有什么方法可以使文本的背景具有一定的空间?我需要一些填充物.
我尝试使用headIndent
,但是那样会将带有背景的文本移到右侧,而不仅仅是文本.因此,它对于填充没有用.
The text is very close to the edge of the background. Is there any way to get the background of the text to have some space to the text? I need some padding.
I tried using headIndent
but that would move the text with it's background to the right, not just the text. Therefor it is not useful for padding.
推荐答案
文本应具有背景和自定义填充,以使文本到背景边缘有一点空间.
The text should have a background and a custom padding, so that the text has a little bit of space to the background's edge.
我发现最好的方法是使用TextKit ,虽然有点麻烦,但它是完全模块化的,是为此目的而制作的.
在我看来,不是由TextView本身在其draw
方法中绘制矩形,这是LayoutManager的工作.
The best way I found is using TextKit, it's a little bit cumbersome but it's completely modular and is made for this purpose.
In my view, it isn't up to the TextView itself to draw the rectangles in its draw
method, that's the LayoutManager's work.
此后提供了项目中使用的所有类,以简化复制粘贴(Swift 5.1-iOS 13)的工作.
The entire classes used in the project are provided hereafter in order to ease the work with copy-paste (Swift 5.1 - iOS 13).
AppDelegate.swift 存储该属性以获取文本,无论您在应用程序中的任何位置.
AppDelegate.swift stores the property to get the text wherever you are in the app.
class AppDelegate: UIResponder, UIApplicationDelegate {
lazy var TextToBeRead: NSAttributedString = {
var text: String
if let filepath = Bundle.main.path(forResource: "TextToBeRead", ofType: "txt") {
do { text = try String(contentsOfFile: filepath) }
catch { text = "E.R.R.O.R." }
} else { text = "N.O.T.H.I.N.G." }
return NSAttributedString(string: text)
}()
}
ViewController.swift ⟹在全屏模式下只能显示一个文本.
ViewController.swift ⟹ only one single text view at full screen.
class ViewController: UIViewController, NSLayoutManagerDelegate {
@IBOutlet weak var myTextView: UITextView!
let textStorage = MyTextStorage()
let layoutManager = MyLayoutManager()
override func viewDidLoad() {
super.viewDidLoad()
self.layoutManager.delegate = self
self.textStorage.addLayoutManager(self.layoutManager)
self.layoutManager.addTextContainer(myTextView.textContainer)
let appDelegate = UIApplication.shared.delegate as? AppDelegate
self.textStorage.replaceCharacters(in: NSRange(location: 0, length: 0),
with: (appDelegate?.TextToBeRead.string)!)
}
func layoutManager(_ layoutManager: NSLayoutManager,
lineSpacingAfterGlyphAt glyphIndex: Int,
withProposedLineFragmentRect rect: CGRect) -> CGFloat { return 20.0 }
func layoutManager(_ layoutManager: NSLayoutManager,
paragraphSpacingAfterGlyphAt glyphIndex: Int,
withProposedLineFragmentRect rect: CGRect) -> CGFloat { return 30.0 }
}
MyTextStorage.swift
class MyTextStorage: NSTextStorage {
var backingStorage: NSMutableAttributedString
override init() {
backingStorage = NSMutableAttributedString()
super.init()
}
required init?(coder: NSCoder) {
backingStorage = NSMutableAttributedString()
super.init(coder: coder)
}
// Overriden GETTERS
override var string: String {
get { return self.backingStorage.string }
}
override func attributes(at location: Int,
effectiveRange range: NSRangePointer?) -> [NSAttributedString.Key : Any] {
return backingStorage.attributes(at: location, effectiveRange: range)
}
// Overriden SETTERS
override func replaceCharacters(in range: NSRange, with str: String) {
backingStorage.replaceCharacters(in: range, with: str)
self.edited(.editedCharacters,
range: range,
changeInLength: str.count - range.length)
}
override func setAttributes(_ attrs: [NSAttributedString.Key : Any]?, range: NSRange) {
backingStorage.setAttributes(attrs, range: range)
self.edited(.editedAttributes,
range: range,
changeInLength: 0)
}
}
MyLayoutManager.swift
import CoreGraphics //Important to draw the rectangles
class MyLayoutManager: NSLayoutManager {
override init() { super.init() }
required init?(coder: NSCoder) { super.init(coder: coder) }
override func drawBackground(forGlyphRange glyphsToShow: NSRange, at origin: CGPoint) {
super.drawBackground(forGlyphRange: glyphsToShow, at: origin)
self.enumerateLineFragments(forGlyphRange: glyphsToShow) { (rect, usedRect, textContainer, glyphRange, stop) in
var lineRect = usedRect
lineRect.size.height = 30.0
let currentContext = UIGraphicsGetCurrentContext()
currentContext?.saveGState()
currentContext?.setStrokeColor(UIColor.red.cgColor)
currentContext?.setLineWidth(1.0)
currentContext?.stroke(lineRect)
currentContext?.restoreGState()
}
}
}
...这是最后的样子:
... and here's what it looks like in the end:
只能自定义颜色并调整少量参数以坚持您的项目,但这是在文本和背景前后显示 NSAttributedString和填充的基本原理……更多多于2行.
There's only to customize the colors and adjust few parameters to stick to your project but this is the rationale to display a NSAttributedString with padding before and after the text and a background... for much more than 2 lines if need be.
这篇关于NSAttributedString,在文本和背景前后有填充的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!