59

I'm already pretty sure that it can't be done with any public API, but I still want to ask:

Is there any way to change the line height in a UITextView?

Would be enough to do it statically, no need to change it at runtime. The problem is that the default line height is just WAY too small. Text will look extremely compressed and is a nightmare when trying to write longer texts.

thanks, Max

EDIT: I know that there is UIWebView and that it's nice and can do styling etc. But it's not editable. I need a editable text component with acceptable line height. That thing from the Omni Frameworks doesn't help either, as it's too slow and doesn't feel right...

Max Seelemann
  • 9,344
  • 4
  • 34
  • 40

9 Answers9

114

After iOS 7, the styleString approach no longer works.

Two new alternatives are available.

Firstly, TextKit; a powerful new layout engine. To change line spacing, set the UITextView's layout manager's delegate:

textView.layoutManager.delegate = self; // you'll need to declare you implement the NSLayoutManagerDelegate protocol

Then override this delegate method:

- (CGFloat)layoutManager:(NSLayoutManager *)layoutManager lineSpacingAfterGlyphAtIndex:(NSUInteger)glyphIndex withProposedLineFragmentRect:(CGRect)rect
{
    return 20; // For really wide spacing; pick your own value
}

Secondly, iOS 7 now supports NSParagraphStyle's lineSpacing. This gives even more control, e.g. first line indentation, and calculation of a bounding rect. So alternatively...

NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
paragraphStyle.headIndent = 15; // <--- indention if you need it
paragraphStyle.firstLineHeadIndent = 15;

paragraphStyle.lineSpacing = 7; // <--- magic line spacing here!

NSDictionary *attrsDictionary =
@{ NSParagraphStyleAttributeName: paragraphStyle }; // <-- there are many more attrs, e.g NSFontAttributeName

self.textView.attributedText = [[NSAttributedString alloc] initWithString:@"Hello World over many lines!" attributes:attrsDictionary];

FWIW, the old contentInset method to align the text along the left edge of UITextView is also no use under iOS7. Instead, to remove the margin:

textView.textContainer.lineFragmentPadding = 0;
Graham Perks
  • 23,007
  • 8
  • 61
  • 83
25

Note: this is not available in iOS7:


I have discovered that you can create a subclass that re-implements [UITextView styleString]:

@interface UITextView ()
- (id)styleString; // make compiler happy
@end

@interface MBTextView : UITextView
@end
@implementation MBTextView
- (id)styleString {
    return [[super styleString] stringByAppendingString:@"; line-height: 1.2em"];
}
@end

This is not private API usage: it is just subclassing. It's possible Apple may disagree of course (though considering how we all used to swizzle everything in order to customize UIKit appearance, I feel that this kind of “private” usage is not what Apple object to), but it's such an easy way to achieve the goals of this question that you may as well try it. Should the app be rejected you can spend the (probably significant) time on a more difficult solution.

Fattie
  • 27,874
  • 70
  • 431
  • 719
mxcl
  • 26,392
  • 12
  • 99
  • 98
21

In the Attribute Inspector for the UITextView instead change the property Text to Attributed (from Plain), and click the "more" button, there you can set the line height and spacing.

UITextView's Attribute Inspector

Vegard
  • 4,352
  • 1
  • 27
  • 25
19

The only solution we've found and the one we've chosen: create a custom font. Sound silly but seems to be the only realistic way.

Max Seelemann
  • 9,344
  • 4
  • 34
  • 40
  • @ Max Seelemann: Have you ever found another solution to this problem or is this the only way? I guess FontLab will do and then simply resize font to base line and you'll have a better spacing, right? Thanks! – n.evermind Apr 01 '11 at 14:06
  • @n.evermind No we still use this solution. And as far as I can tell, iOS 5 will not change anything to that... – Max Seelemann Jul 14 '11 at 12:24
  • That's quite annoying. Do you thin coreText is an alternative? I just can't be bothered learning something new yet again... for something so simple (line spacing). – n.evermind Jul 14 '11 at 12:40
  • Well you *can* consider Core Text. But that has many more disadvantages, as I described here: http://stackoverflow.com/questions/3642540/uitextview-with-syntax-highlighting/3643425#3643425 – Max Seelemann Jul 14 '11 at 15:05
  • Can u explain how to create custom font to change interline spacing? – Valerii Pavlov Aug 10 '12 at 14:06
  • 1
    You have to use a font editing software like [FontLab Studio](http://www.fontlab.com/font-editor/fontlab-studio/). As I didn't do the customization, I can't tell which exact tool we used and how to do it. HTH anyways. – Max Seelemann Aug 23 '12 at 10:33
10

The UITextView subclass override of styleString only works if you define a category on UITextView that defines styleString, otherwise you get a compile error. For example, in your UITextView subclass:

#import "SomeDangTextView.h"

@interface UITextView ()

- (id)styleString;

@end

@implementation SomeDangTextView

- (id)styleString {
    return [[super styleString] stringByAppendingString:@"; line-height: 1.5em"];
}

@end
user1172004
  • 177
  • 2
  • 4
  • I tried Max Howell's answer before without success, dinked around in Apple's docs for awhile, and gave up. But this (defining a category on UITextView) is what got it to work. Thanks very much. – Steve Cotner Mar 19 '12 at 07:52
  • This worries me as a non-public (aka private) API. I dont see it documented: http://developer.apple.com/library/ios/#DOCUMENTATION/UIKit/Reference/UITextView_Class/Reference/UITextView.html – Mazyod May 31 '12 at 10:48
  • This still isn't working for me. How do you define -(id)styleString in the UITextView category? – Roger O'Brien Apr 18 '13 at 17:11
5

This question is almost 10 years old but this is how it's done:

Just implement the following method of UITextViewDelegate and set your attributes:

let textViewAttributes: [NSAttributedString.Key:Any] = [
    .font: UIFont.systemFont(ofSize: 15, weight: .medium),
    .foregroundColor: UIColor.black,
    .paragraphStyle: {
        let paragraph = NSMutableParagraphStyle()
        paragraph.lineSpacing = 4
        return paragraph
    }()
]

func textViewDidBeginEditing(_ textView: UITextView) {
    textView.typingAttributes = textViewAttributes
}

It's important to add these attributes on textViewDidBeginEditing as the dictionary gets reset every time the text selection changes. More info can be found on the official documentation.

Raphael
  • 7,972
  • 14
  • 62
  • 83
  • It doesn't seem to be necessary to do this as late as when the `textViewDidBeginEditing` delegate method is called. I'm setting `typingAttributes` right after instantiating the UITextView and getting the desired effect. – Robin Daugherty Nov 07 '21 at 17:47
-1

This class does not support multiple styles for text. The font, color, and text alignment attributes you specify always apply to the entire contents of the text view. To display more complex styling in your application, you need to use a UIWebView object and render your content using HTML.

Ritesh verma
  • 101
  • 1
  • 8
  • -1 That's not what the question was about. Plus it's also *stated* there that UIWebView is no choice since it does not offer editing. – Max Seelemann Mar 26 '13 at 10:24
-1

According to Apple's Documentation you can use a UIWebView.

This class does not support multiple styles for text. The font, color, and text alignment attributes you specify always apply to the entire contents of the text view. To display more complex styling in your application, you need to use a UIWebView object and render your content using HTML.

Jordan
  • 21,746
  • 10
  • 51
  • 63
  • It is if you intercept the tap on the WebView, show a UITextView on top, edit and save into the UIWebView ;) – Jordan Sep 21 '10 at 17:11
  • right, but as the primary usage is while editing this still is not the best option. And there's another problem: It's very very hard (if at all possible) to manually place the insertion point in a UITextView upon an event. This, however, would be needed to switch views. :( – Max Seelemann Sep 22 '10 at 00:26
  • `UITextField` adopts `UITextInput` which includes `- (UITextPosition *)closestPositionToPoint:(CGPoint)point`, etc... sounds like what you need? – nielsbot Dec 20 '11 at 21:51
-4

Use OHAttributedLabel Lib. This will solve all the problem you mentioned.

AAV
  • 3,785
  • 8
  • 32
  • 59
  • 1
    Sorry, but it doesn't. `OHAttributedLabel` is a UILabel -- a **noneditable** component. The question is about `UITextView` which is editable. – Max Seelemann Jan 17 '12 at 11:59