NSAttributedString,在文本和背景前后有填充 [英] NSAttributedString with padding before and after the text and a background

查看:117
本文介绍了NSAttributedString,在文本和背景前后有填充的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想使用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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆