Ios UITextView/UILabel,带背景,但行间距

Ios UITextView/UILabel,带背景,但行间距,ios,cocoa-touch,uikit,Ios,Cocoa Touch,Uikit,如何在iOS中实现这种风格 我尝试使用nsAttributeString并将NSBackgroundColorAttributeName设置为blackColor,但它在两行之间没有clearColor空格。我还尝试将行距设置为NSMutableParagraphStyle,但该样式看起来仍然像一个大的黑色块 我只需要一个提示,告诉你怎么走。。。谢谢 换句话说,不是这样的: self.layoutManager.delegate = self; 编辑: 根据您的要求,您需要为每一行使用不同的

如何在iOS中实现这种风格

我尝试使用
nsAttributeString
并将
NSBackgroundColorAttributeName
设置为
blackColor
,但它在两行之间没有
clearColor
空格。我还尝试将
行距
设置为
NSMutableParagraphStyle
,但该样式看起来仍然像一个大的黑色块

我只需要一个提示,告诉你怎么走。。。谢谢

换句话说,不是这样的:

self.layoutManager.delegate = self;

编辑

根据您的要求,您需要为
每一行使用
不同的UILabel
,并通过
设置帧来管理间距


对于我来说,在
多行UILabel
中的
行之间有
间距

 NSMutableAttributedString* attrString = [[NSMutableAttributedString alloc] initWithString:yourlblText.text];
NSMutableParagraphStyle *style = [[NSMutableParagraphStyle alloc] init];
[style setLineSpacing:16];
[attrString addAttribute:NSParagraphStyleAttributeName
                   value:style
                   range:NSMakeRange(0, yourlblText.text.length)];
yourlblText.attributedText = attrString;

同时检查一下

经过一些研究,我找到了我所需要的最佳解决方案。 下面的解决方案只有iOS7+

首先,我们将其添加到
UITextView
子类的
-(void)drawRect:(CGRect)rect

- (void)drawRect:(CGRect)rect    

    /// Position each line behind each line.
    [self.layoutManager enumerateLineFragmentsForGlyphRange:NSMakeRange(0, self.text.length) usingBlock:^(CGRect rect, CGRect usedRect, NSTextContainer *textContainer, NSRange glyphRange, BOOL *stop) {

        /// The frame of the rectangle.
        UIBezierPath *rectanglePath = [UIBezierPath bezierPathWithRect:CGRectMake(usedRect.origin.x, usedRect.origin.y+3, usedRect.size.width, usedRect.size.height-4)];

        /// Set the background color for each line.
        [UIColor.blackColor setFill];

        /// Build the rectangle.
        [rectanglePath fill];
    }];
 }];
然后,我们设置UITextView的行距:

- (CGFloat)layoutManager:(NSLayoutManager *)layoutManager lineSpacingAfterGlyphAtIndex:(NSUInteger)glyphIndex withProposedLineFragmentRect:(CGRect)rect
{
    return 15;
}
仅当设置了
NSLayoutManagerDelegate
时,才会调用上述方法。您可以在
init
initWithFrame
initWithCode
方法中这样做:

self.layoutManager.delegate = self;
另外,不要忘记声明您的子类是
.h
文件中的委托:

@interface YOUR_SUBCLASS_OF_UITEXTVIEW : UITextView <NSLayoutManagerDelegate>
@接口您的\u UITEXTVIEW的子类\u:UITEXTVIEW

只需添加一个具有所需行高的段落样式的新空行即可。在我的Swift示例中,我只是将interlineAttributedString附加到原始attributedString:

let interlineStyle = NSMutableParagraphStyle()
interlineStyle.maximumLineHeight = 5.0
let interlineAttributedString = NSMutableAttributedString(string: "\n", attributes: [NSParagraphStyleAttributeName: interlineStyle])
attributedString.appendAttributedString(interlineAttributedString)
额外\n也可以是原始AttributeString的一部分。在这种情况下,您将使用具有适当范围的
addAttribute


我只在iOS 9上测试过,但我很确定它在以前的iOS版本上运行良好。它在UITextView中工作得非常好。它比使用许多UILabel或绘制背景矩形更容易实现。

Swift 3版本@Fabio的解决方案:

class splitedTextView: UITextView, NSLayoutManagerDelegate {

    override func awakeFromNib() {
        self.layoutManager.delegate = self
    }

    override func draw(_ rect: CGRect) {
        self.layoutManager.enumerateLineFragments(forGlyphRange: NSMakeRange(0, self.text.characters.count)) { (rect, usedRect, textContainer, glyphRange, Bool) in
            let rectanglePath = UIBezierPath(rect: CGRect(x: usedRect.origin.x, y: usedRect.origin.y+3, width: usedRect.size.width, height: usedRect.size.height-4))
            UIColor.black.setFill()
            rectanglePath.fill()
        }
    }

    func layoutManager(_ layoutManager: NSLayoutManager, lineSpacingAfterGlyphAt glyphIndex: Int, withProposedLineFragmentRect rect: CGRect) -> CGFloat {
        return 15
    }
}
您还可以在
textView
子类中创建一个变量来管理矩形的颜色:

var color: UIColor?
然后在
textView
子类中使用它而不是默认的黑色:

self.color?.setFill()

如果您这样做,请不要忘记使用
setNeedsDisplay()
重新绘制
textView
并应用自定义颜色。

我的方法是创建UIView并在其上添加标签,约束到垂直顶部,然后使用字体大小和行高计算行数,然后为每行创建CALayer并将其添加到UIView

你可以检查我的实现


对于iOS 11,属性字符串与这种UI行为存在问题。我已经创建了带有空格和bgColor属性的UITextView子类。希望这也有帮助。答案只是@Fabio解决方案的扩展

class SpacingTextView: UITextView, NSLayoutManagerDelegate {

private var textHolder: String?
var spacing: CGFloat = 3
var bgColor: UIColor = .white

override var attributedText: NSAttributedString! {
    didSet {
        self.textHolder = self.attributedText.string
        self.setNeedsDisplay()
    }
}

override var text: String! {
    didSet {
        self.textHolder = self.attributedText.string
        self.setNeedsDisplay()
    }
}

override init(frame: CGRect, textContainer: NSTextContainer?) {
    super.init(frame: frame, textContainer: textContainer)
    self.configure()
}

required init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
    self.configure()
}

private func configure() {
    self.layoutManager.delegate = self
    self.backgroundColor = .clear
}

override func draw(_ rect: CGRect) {
    super.draw(rect)
    guard let txt = self.textHolder else {
        return
    }
    let textRange = NSRange(location: 0, length: txt.count)

    self.layoutManager.enumerateLineFragments(forGlyphRange: textRange) { (rect, usedRect, _, _, _) in
        var bgRect = usedRect
        bgRect.origin.y += self.spacing / 2
        bgRect.size.height -= self.spacing
        let bezierPath = UIBezierPath(rect: bgRect)
        self.bgColor.setFill()
        bezierPath.fill()
        bezierPath.close()
    }
}

func layoutManager(_ layoutManager: NSLayoutManager,
                   lineSpacingAfterGlyphAt glyphIndex: Int, withProposedLineFragmentRect rect: CGRect) -> CGFloat {
    return rect.size.height
}

func layoutManager(_ layoutManager: NSLayoutManager,
                   shouldUse action: NSLayoutManager.ControlCharacterAction,
                   forControlCharacterAt charIndex: Int) -> NSLayoutManager.ControlCharacterAction {
    return .lineBreak
}
}

你好,王子,谢谢你的回答!但是,您的代码生成的正是我刚才添加到问题中的第二幅图像中显示的内容。两行之间没有透明的空格。嘿@Prince,再次感谢你的回答!您创建不同的
UILabel
的解决方案非常肮脏,但如果我想满足要求,我想我必须这样做。还在寻找更好的。但无论如何,感谢您提供了一个解决方案!干杯谢谢,但它只适用于两行文字,我想用于多行文字。@Fabio您也可以提供一个swift版本吗?@user3306145:如果您仍然需要,有一个swift版本可以处理整个文字⟹