如何解决使用背景色向 UILabel 添加填充
我有一个包含 NSAttributedString 的多行 UILabel,它应用了背景颜色来实现上述效果。
这很好,但我需要在标签内填充以在左侧留出一些空间。 SO 上还有其他帖子解决了这个问题,例如通过子类化 UILabel 来添加 UIEdgeInsets。然而,这只是为我添加了标签外部的填充。
编辑:如果这令人困惑,请道歉,但最终目标是这样的......
解决方法
我使用了一种不同的方式。首先,我从 UILabel
中获取所有行,并在每一行的起始位置添加额外的空格。要从 UILabel
获取所有行,我只需修改此链接中的代码 (https://stackoverflow.com/a/55156954/14733292)
最终 extension UILabel
代码:
extension UILabel {
var addWhiteSpace: String {
guard let text = text,let font = font else { return "" }
let ctFont = CTFontCreateWithName(font.fontName as CFString,font.pointSize,nil)
let attStr = NSMutableAttributedString(string: text)
attStr.addAttribute(kCTFontAttributeName as NSAttributedString.Key,value: ctFont,range: NSRange(location: 0,length: attStr.length))
let frameSetter = CTFramesetterCreateWithAttributedString(attStr as CFAttributedString)
let path = CGMutablePath()
path.addRect(CGRect(x: 0,y: 0,width: self.frame.size.width,height: CGFloat.greatestFiniteMagnitude),transform: .identity)
let frame = CTFramesetterCreateFrame(frameSetter,CFRangeMake(0,0),path,nil)
guard let lines = CTFrameGetLines(frame) as? [Any] else { return "" }
return lines.map { line in
let lineRef = line as! CTLine
let lineRange: CFRange = CTLineGetStringRange(lineRef)
let range = NSRange(location: lineRange.location,length: lineRange.length)
return " " + (text as NSString).substring(with: range)
}.joined(separator: "\n")
}
}
使用:
let labelText = yourLabel.addWhiteSpace
let attributedString = NSMutableAttributedString(string: labelText)
attributedString.addAttribute(NSAttributedString.Key.backgroundColor,value: UIColor.red,length: labelText.count))
yourLabel.attributedText = attributedString
yourLabel.backgroundColor = .yellow
已编辑:
上面的代码在某些时候可以工作,但还不够。所以我创建了一个类并添加了填充和一个形状矩形层。
class AttributedPaddingLabel: UILabel {
private let leftShapeLayer = CAShapeLayer()
var leftPadding: CGFloat = 5
var attributedTextColor: UIColor = .red
override func awakeFromNib() {
super.awakeFromNib()
self.addLeftSpaceLayer()
self.addAttributed()
}
override func drawText(in rect: CGRect) {
let insets = UIEdgeInsets(top: 0,left: leftPadding,bottom: 0,right: 0)
super.drawText(in: rect.inset(by: insets))
}
override var intrinsicContentSize: CGSize {
let size = super.intrinsicContentSize
return CGSize(width: size.width + leftPadding,height: size.height)
}
override var bounds: CGRect {
didSet {
preferredMaxLayoutWidth = bounds.width - leftPadding
}
}
override func draw(_ rect: CGRect) {
super.draw(rect)
leftShapeLayer.path = UIBezierPath(rect: CGRect(x: 0,width: leftPadding,height: rect.height)).cgPath
}
private func addLeftSpaceLayer() {
leftShapeLayer.fillColor = attributedTextColor.cgColor
self.layer.addSublayer(leftShapeLayer)
}
private func addAttributed() {
let lblText = self.text ?? ""
let attributedString = NSMutableAttributedString(string: lblText)
attributedString.addAttribute(NSAttributedString.Key.backgroundColor,value: attributedTextColor,length: lblText.count))
self.attributedText = attributedString
}
}
使用方法:
class ViewController: UIViewController {
@IBOutlet weak var lblText: AttributedPaddingLabel!
override func viewDidLoad() {
super.viewDidLoad()
lblText.attributedTextColor = .green
lblText.leftPadding = 10
}
}
,
基于此处提供的答案:https://stackoverflow.com/a/59216224/6257435
只是为了演示(几个硬编码值,理想情况下是动态/计算的):
class ViewController: UIViewController,NSLayoutManagerDelegate {
var myTextView: UITextView!
let textStorage = MyTextStorage()
let layoutManager = MyLayoutManager()
override func viewDidLoad() {
super.viewDidLoad()
myTextView = UITextView()
myTextView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(myTextView)
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
myTextView.topAnchor.constraint(equalTo: g.topAnchor,constant: 40.0),myTextView.leadingAnchor.constraint(equalTo: g.leadingAnchor,myTextView.widthAnchor.constraint(equalToConstant: 248.0),myTextView.heightAnchor.constraint(equalToConstant: 300.0),])
self.layoutManager.delegate = self
self.textStorage.addLayoutManager(self.layoutManager)
self.layoutManager.addTextContainer(myTextView.textContainer)
let quote = "This is just some sample text to demonstrate the word wrapping with padding at the beginning (leading) and ending (trailing) of the lines of text."
self.textStorage.replaceCharacters(in: NSRange(location: 0,length: 0),with: quote)
guard let font = UIFont(name: "TimesNewRomanPSMT",size: 24.0) else {
fatalError("Could not instantiate font!")
}
let attributes: [NSAttributedString.Key : Any] = [NSAttributedString.Key.font: font]
self.textStorage.setAttributes(attributes,length: quote.count))
myTextView.isUserInteractionEnabled = false
// so we can see the frame of the textView
myTextView.backgroundColor = .systemTeal
}
func layoutManager(_ layoutManager: NSLayoutManager,lineSpacingAfterGlyphAt glyphIndex: Int,withProposedLineFragmentRect rect: CGRect) -> CGFloat { return 14.0 }
func layoutManager(_ layoutManager: NSLayoutManager,paragraphSpacingAfterGlyphAt glyphIndex: Int,withProposedLineFragmentRect rect: CGRect) -> CGFloat { return 14.0 }
}
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,changeInLength: 0)
}
}
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 = 41.0
let currentContext = UIGraphicsGetCurrentContext()
currentContext?.saveGState()
// outline rectangles
//currentContext?.setStrokeColor(UIColor.red.cgColor)
//currentContext?.setLineWidth(1.0)
//currentContext?.stroke(lineRect)
// filled rectangles
currentContext?.setFillColor(UIColor.orange.cgColor)
currentContext?.fill(lineRect)
currentContext?.restoreGState()
}
}
}
输出(青色背景显示框架):
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。