15

How can I increase line space in UITextView so that it looks like "Notes" app in iPhone?

swiftBoy
  • 35,607
  • 26
  • 136
  • 135
Satyam
  • 15,493
  • 31
  • 131
  • 244
  • 1
    For Swift 4 and iOS 11, see [this answer](https://stackoverflow.com/a/48360549/1966109) that shows up to 3 different ways to solve your problem. – Imanou Petit Jan 21 '18 at 23:42

8 Answers8

33

With Swift 4 and iOS 11, according to your needs, you can choose one of the 3 following implementations in order to solve your problem.


#1. Using String and UIFontDescriptorSymbolicTraits traitLooseLeading property

traitLooseLeading has the following declaration:

The font uses looser leading values.

static var traitLooseLeading: UIFontDescriptorSymbolicTraits { get }

The following code shows how to implement traitLooseLeading in order to have a looser font leading for your UItextView.

import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        let textView = UITextView()
        view.addSubview(textView)

        textView.text = """
        Lorem ipsum
        Dolor sit amet,
        consectetur adipiscing elit
        """

        if let fontDescriptor = UIFontDescriptor
            .preferredFontDescriptor(withTextStyle: UIFontTextStyle.body)
            .withSymbolicTraits(UIFontDescriptorSymbolicTraits.traitLooseLeading) {
            let looseLeadingFont = UIFont(descriptor: fontDescriptor, size: 0)
            textView.font = looseLeadingFont
        }

        // Layout textView
        textView.translatesAutoresizingMaskIntoConstraints = false
        textView.topAnchor.constraint(equalTo: view.readableContentGuide.topAnchor).isActive = true
        textView.bottomAnchor.constraint(equalTo: view.readableContentGuide.bottomAnchor).isActive = true
        textView.leadingAnchor.constraint(equalTo: view.readableContentGuide.leadingAnchor).isActive = true
        textView.trailingAnchor.constraint(equalTo: view.readableContentGuide.trailingAnchor).isActive = true
    }

}

#2. Using NSAttributedString and NSMutableParagraphStyle lineSpacing property

lineSpacing has the following declaration:

The distance in points between the bottom of one line fragment and the top of the next.

var lineSpacing: CGFloat { get set }

The following code shows how to implement lineSpacing in order to have a specific line spacing for some attributed text in your UItextView.

import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        let string = """
        Lorem ipsum
        Dolor sit amet,
        consectetur adipiscing elit
        """

        let paragraphStyle = NSMutableParagraphStyle()
        paragraphStyle.lineSpacing = 15

        let attributes: [NSAttributedStringKey: Any] = [NSAttributedStringKey.paragraphStyle: paragraphStyle]
        let attributedString = NSAttributedString(string: string, attributes: attributes)

        let textView = UITextView()
        textView.attributedText = attributedString
        view.addSubview(textView)

        // Layout textView
        textView.translatesAutoresizingMaskIntoConstraints = false
        textView.topAnchor.constraint(equalTo: view.readableContentGuide.topAnchor).isActive = true
        textView.bottomAnchor.constraint(equalTo: view.readableContentGuide.bottomAnchor).isActive = true
        textView.leadingAnchor.constraint(equalTo: view.readableContentGuide.leadingAnchor).isActive = true
        textView.trailingAnchor.constraint(equalTo: view.readableContentGuide.trailingAnchor).isActive = true
    }

}

#3. Using String and NSLayoutManagerDelegate protocol layoutManager(_:lineSpacingAfterGlyphAt:withProposedLineFragmentRect:) method

layoutManager(_:lineSpacingAfterGlyphAt:withProposedLineFragmentRect:) has the following declaration:

Returns the spacing after the line ending with the given glyph index. [...] This message is sent while each line is laid out to enable the layout manager delegate to customize the shape of line.

optional func layoutManager(_ layoutManager: NSLayoutManager, lineSpacingAfterGlyphAt glyphIndex: Int, withProposedLineFragmentRect rect: CGRect) -> CGFloat

The following code shows how to implement layoutManager(_:lineSpacingAfterGlyphAt:withProposedLineFragmentRect:) in order to have a specific line spacing for your UItextView.

import UIKit

class ViewController: UIViewController, NSLayoutManagerDelegate {

    override func viewDidLoad() {
        super.viewDidLoad()

        let textView = UITextView()
        textView.layoutManager.delegate = self
        view.addSubview(textView)

        textView.text = """
        Lorem ipsum
        Dolor sit amet,
        consectetur adipiscing elit
        """

        // Layout textView
        textView.translatesAutoresizingMaskIntoConstraints = false
        textView.topAnchor.constraint(equalTo: view.readableContentGuide.topAnchor).isActive = true
        textView.bottomAnchor.constraint(equalTo: view.readableContentGuide.bottomAnchor).isActive = true
        textView.leadingAnchor.constraint(equalTo: view.readableContentGuide.leadingAnchor).isActive = true
        textView.trailingAnchor.constraint(equalTo: view.readableContentGuide.trailingAnchor).isActive = true
    }

    // MARK: - NSLayoutManagerDelegate

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

}

As an alternative to the previous code, the following code shows how to implement layoutManager(_:lineSpacingAfterGlyphAt:withProposedLineFragmentRect:) in a UITextView subclass.

import UIKit

class LineSpacingTextView: UITextView, NSLayoutManagerDelegate {

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

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    // MARK: - NSLayoutManagerDelegate

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

}
import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        let textView = LineSpacingTextView()
        view.addSubview(textView)

        textView.text = """
        Lorem ipsum
        Dolor sit amet,
        consectetur adipiscing elit
        """

        // Layout textView
        textView.translatesAutoresizingMaskIntoConstraints = false
        textView.topAnchor.constraint(equalTo: view.readableContentGuide.topAnchor).isActive = true
        textView.bottomAnchor.constraint(equalTo: view.readableContentGuide.bottomAnchor).isActive = true
        textView.leadingAnchor.constraint(equalTo: view.readableContentGuide.leadingAnchor).isActive = true
        textView.trailingAnchor.constraint(equalTo: view.readableContentGuide.trailingAnchor).isActive = true
    }

}
Imanou Petit
  • 89,880
  • 29
  • 256
  • 218
25

Well now on iOS6, there is a possibility, using NSParagraphStyle, but it is very poorly documented and seems to work seldomly.

I'm currently working about it like this:

UITextView *lab = [LocalTexts objectAtIndex:j];

NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
paragraphStyle.lineHeightMultiple = 50.0f;
paragraphStyle.maximumLineHeight = 50.0f;
paragraphStyle.minimumLineHeight = 50.0f;

NSString *string = lab.text;
NSDictionary *ats = @{
    NSFontAttributeName: [UIFont fontWithName:@"DIN Medium" size:16.0f],
    NSParagraphStyleAttributeName: paragraphStyle, 
};

lab.attributedText = [[NSAttributedString alloc] initWithString:string attributes:ats];

Problem is that when you set the Font, the line height stops working. Very odd. I haven't found a fix for it yet.

Also you can create a custom Attributed CoreText view... but it's a bit more technical, you can find a demo of how it is done here

Well I hope something helps.

ricardopereira
  • 11,118
  • 5
  • 63
  • 81
  • Especially if it is poorly documented you may wish to link to any good documentation you *have* found – Hawken Oct 21 '12 at 04:41
11

To change line spacing:

NSString *textViewText =self.myTextView.text;

NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:textViewText];
NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
paragraphStyle.lineSpacing = 30;
NSDictionary *dict = @{NSParagraphStyleAttributeName : paragraphStyle };
[attributedString addAttributes:dict range:NSMakeRange(0, [textViewText length])];

self.myTextView.attributedText = attributedString;
sash
  • 8,423
  • 5
  • 63
  • 74
  • Confirmed working. To note, you may need to go negative to get exactly what you want, if it seems like going as low as 1 isn't making a difference. – Will Apr 28 '15 at 06:24
10

A look at the documentation for UITextView is sufficient to determine that changing the line spacing is not supported by that control.

For iOS 6 and above:

There is a possibility, using NSParagraphStyle,

NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
paragraphStyle.lineHeightMultiple = 50.0f;
paragraphStyle.maximumLineHeight = 50.0f;
paragraphStyle.minimumLineHeight = 50.0f;
NSString *string = @"your paragraph here";
NSDictionary *attribute = @{
   NSParagraphStyleAttributeName : paragraphStyle, 
   };
[textview setFont:[uifont fontwithname:@"Arial" size:20.0f]];
textview.attributedText = [[NSAttributedString alloc] initWithString:string attributes:attribute];
KingofBliss
  • 15,055
  • 6
  • 50
  • 72
6

For Swift 2.2

let paragraphStyle: NSMutableParagraphStyle = NSMutableParagraphStyle()
paragraphStyle.lineHeightMultiple = 20.0
paragraphStyle.maximumLineHeight = 20.0
paragraphStyle.minimumLineHeight = 20.0
let ats = [NSFontAttributeName: UIFont(name: "Helvetica Neue", size: 11.0)!, NSParagraphStyleAttributeName: paragraphStyle]
cell.textView.attributedText = NSAttributedString(string: "you string here", attributes: ats)
swiftBoy
  • 35,607
  • 26
  • 136
  • 135
4

If the end result is to increase line spacing, you can do it directly in interface builder. Set the Text property to "Attributed" and then click the ... on the right. Setting the Spacing property there should update your line spacing correctly.

mcfroob
  • 1,044
  • 1
  • 11
  • 13
2

In several answers above attribute lineHeightMultiple is misused:

paragraphStyle.lineHeightMultiple = 50.0f;

Following to the official documentation lineHeightMultiple is an multiplier and not an absolute height of a string:

The natural line height of the receiver is multiplied by this factor (if positive) before being constrained by minimum and maximum line height. The default value of this property is 0.0. https://developer.apple.com/library/prerelease/ios/documentation/Cocoa/Reference/ApplicationKit/Classes/NSParagraphStyle_Class/index.html#//apple_ref/occ/instp/NSParagraphStyle/maximumLineHeight

Thereby, the code below:

paragraphStyle.lineHeightMultiple = 50.0f;
paragraphStyle.maximumLineHeight = 50.0f;
paragraphStyle.minimumLineHeight = 50.0f;

is equivalent to

paragraphStyle.lineHeight = 50.0f
0
 NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
   paragraphStyle.lineHeightMultiple = 50.0f;
   paragraphStyle.maximumLineHeight = 50.0f;
   paragraphStyle.minimumLineHeight = 50.0f;
     NSString *string = @"if you want reduce or increase space between lines in uitextview ,you can do this with this,but donot set font on this paragraph , set this on uitextveiw.";

    NSDictionary *ats = @{
   NSParagraphStyleAttributeName : paragraphStyle, 
     };

    [textview setFont:[uifont fontwithname:@"Arial" size:20.0f]];
    textview.attributedText = [[NSAttributedString alloc] initWithString:string attributes:ats];
kshitij godara
  • 1,523
  • 18
  • 30