使用背景色向 UILabel 添加填充

如何解决使用背景色向 UILabel 添加填充

UILabel with background color

我有一个包含 NSAttributedString 的多行 UILabel,它应用了背景颜色来实现上述效果

这很好,但我需要在标签填充以在左侧留出一些空间。 SO 上还有其他帖子解决了这个问题,例如通过子类化 UILabel 来添加 UIEdgeInsets。然而,这只是为我添加标签外部的填充。

关于如何向此标签添加填充有任何建议吗?

编辑:如果这令人困惑,请道歉,但最终目标是这样的......

enter image description here

解决方法

我使用了一种不同的方式。首先,我从 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()
        }
    }
}

输出(青色背景显示框架):

enter image description here

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?
Java在半透明框架/面板/组件上重新绘画。
Java“ Class.forName()”和“ Class.forName()。newInstance()”之间有什么区别?
在此环境中不提供编译器。也许是在JRE而不是JDK上运行?
Java用相同的方法在一个类中实现两个接口。哪种接口方法被覆盖?
Java 什么是Runtime.getRuntime()。totalMemory()和freeMemory()?
java.library.path中的java.lang.UnsatisfiedLinkError否*****。dll
JavaFX“位置是必需的。” 即使在同一包装中
Java 导入两个具有相同名称的类。怎么处理?
Java 是否应该在HttpServletResponse.getOutputStream()/。getWriter()上调用.close()?
Java RegEx元字符(。)和普通点?