NSAttributedString 在文本和背景之前和之后有填充
Posted
技术标签:
【中文标题】NSAttributedString 在文本和背景之前和之后有填充【英文标题】:NSAttributedString with padding before and after the text and a background 【发布时间】:2019-12-09 20:12:51 【问题描述】:我想使用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)
这就是 Playground 预览呈现的内容:
[![游乐场预览][3]][3]
文字非常靠近背景边缘。有什么方法可以让文本的背景为文本留出一些空间?我需要一些填充物。
我尝试使用headIndent
,但这会将带有背景的文本移动到右侧,而不仅仅是文本。因此它对填充没有用。
【问题讨论】:
paragraphStyle.lineSpacing = desiredLineSpacing,开始和结束填充,您可以添加空格或执行类似于***.com/questions/27459746/…的操作 空格在开头和结尾都有效,但是当文本换行时你需要一个。而且因为我不知道文本何时换行(自动布局、文本长度),所以我无法设置空格。另一个问题的方法并不完全符合我的要求。我希望背景只要单个文本行有字符。 UILabel 上的背景具有标签的宽度和单行的单词或字符的宽度。 你不需要 NSAttributedString 来完成这个简单的任务。使用带有约束和 0 行的 UILabel,在代码中为它分配字符串时,只需这样做:label.text = " \(myTextString) "
,并为标签分配背景颜色。
@Starsky 标签的问题是,背景总是与标签的宽度一样宽。但我希望背景只在角色后面可见。所以每一行都有一个背景,只有字符去。 (查看更新的问题以获取图片)
@WalterBeiter:看过***.com/a/28042708/3825084,这可能是解决您问题的好方法?
【参考方案1】:
文本应该有一个背景和一个自定义填充,以便文本在背景边缘有一点空间。
我发现最好的方法是使用 TextKit,它有点麻烦,但它是完全模块化的,专为此目的而设计。
在我看来,TextView 本身不能在其draw
方法中绘制矩形,这是 LayoutManager 的工作。
为了方便使用复制粘贴(Swift 5.1 - ios 13),下文提供了项目中使用的整个类。
AppDelegate.swift 存储用于在应用中的任何位置获取文本的属性。
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 ⟹ 全屏只有一个文本视图。
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()
...最后是这样的:
只需要自定义颜色和调整几个参数以贴合您的项目,但这是在文本和背景前后显示带有填充的 NSAttributedString 的基本原理...还有更多如果需要,超过 2 行。
【讨论】:
以上是关于NSAttributedString 在文本和背景之前和之后有填充的主要内容,如果未能解决你的问题,请参考以下文章
NSAttributedString.h 中文本属性key的说明-06
Attributes:文本属性 和NSAttributedString