Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

UITextView/UILabel with background but with spacing between lines

How can I achieve this kind of style in iOS?

enter image description here

I tried using NSAttributedString and setting NSBackgroundColorAttributeName as blackColor but then it doesn't have a clearColor space between the lines. I also tried to set a lineSpacing to the NSMutableParagraphStyle but still, the style looks like one big black block.

I just need a hint in which way to go... thanks!

In other words, not something like this:

enter image description here

like image 842
Fabio Avatar asked Dec 08 '14 14:12

Fabio


People also ask

How do you control line spacing in UILabel?

"Short answer: you can't. To change the spacing between lines of text, you will have to subclass UILabel and roll your own drawTextInRect, or create multiple labels." This is a really old answer, and other have already addded the new and better way to handle this..

How do you add padding to UILabel?

If you have created an UILabel programmatically, replace the UILabel class with the PaddingLabel and add the padding: // Init Label let label = PaddingLabel() label. backgroundColor = . black label.


2 Answers

After some research I found the best solution for what I needed. The solution below is only iOS7+.

First we add this to - (void)drawRect:(CGRect)rect of your UITextView subclass.

- (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];
    }];
 }];

Then we set the line spacing for the UITextView:

- (CGFloat)layoutManager:(NSLayoutManager *)layoutManager lineSpacingAfterGlyphAtIndex:(NSUInteger)glyphIndex withProposedLineFragmentRect:(CGRect)rect
{
    return 15;
}

The method above is only called if you set the NSLayoutManagerDelegate. You could do that in your init, initWithFrame and initWithCode methods like this:

self.layoutManager.delegate = self;

Also don't forget to declare that your subclass is a delegate in your .h file:

@interface YOUR_SUBCLASS_OF_UITEXTVIEW : UITextView <NSLayoutManagerDelegate>
like image 100
Fabio Avatar answered Sep 27 '22 17:09

Fabio


Swift 3 version of @Fabio's solution :

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
    }
}

You can also create a variable inside your textView subclass to manage rectangle's color :

var color: UIColor?

And then use it instead of default black in your textView subclass :

self.color?.setFill()

Also if you do that, don't forget to use setNeedsDisplay() to redraw your textView and apply your custom color.

like image 28
Skaal Avatar answered Sep 27 '22 17:09

Skaal