[ios] How to lose margin/padding in UITextView?

I have a UITextView in my iOS Application, which displays a large amount of text.

I am then paging this text by using the offset margin parameter of the UITextView.

My problem is that the padding of the UITextView is confusing my calculations as it seems to be different depending on the font size and typeface that I use.

Therefore, I pose the question: Is it possible to remove the padding surrounding the content of the UITextView?

Look forward to hearing your responses!

This question is related to ios iphone cocoa-touch uikit uitextview

The answer is


Doing the inset solution I still had padding on the right side and the bottom. Also text alignment was causing issues. The only sure fire way I found was to put the text view inside another view that is clipped to bounds.


Storyboard or Interface Builder solution using User Defined Runtime Attributes:

Screenshots are of iOS 7.1 & iOS 6.1 with contentInset = {{-10, -5}, {0, 0}}.

User Defined Runtime Attributes

output


On iOS 5 UIEdgeInsetsMake(-8,-8,-8,-8); seems to work great.


[firstNameTextField setContentVerticalAlignment:UIControlContentVerticalAlignmentCenter];

Here's an easy little extension that will remove Apple's default margin from every text view in your app.

Note: Interface Builder will still show the old margin, but your app will work as expected.

extension UITextView {

   open override func awakeFromNib() {
      super.awakeFromNib();
      removeMargins();
   }

   /** Removes the Apple textview margins. */
   public func removeMargins() {
      self.contentInset = UIEdgeInsetsMake(
         0, -textContainer.lineFragmentPadding,
         0, -textContainer.lineFragmentPadding);
   }
}

This workaround was written in 2009 when IOS 3.0 was released. It no longer applies.

I ran into the exact same problem, in the end I had to wind up using

nameField.contentInset = UIEdgeInsetsMake(-4,-8,0,0);

where nameField is a UITextView. The font I happened to be using was Helvetica 16 point. Its only a custom solution for the particular field size I was drawing. This makes the left offset flush with the left side, and the top offset where I want it for the box its draw in.

In addition, this only seems to apply to UITextViews where you are using the default aligment, ie.

nameField.textAlignment = NSTextAlignmentLeft;

Align to the right for example and the UIEdgeInsetsMake seems to have no impact on the right edge at all.

At very least, using the .contentInset property allows you to place your fields with the "correct" positions, and accommodate the deviations without offsetting your UITextViews.


For swift 4, Xcode 9

Use the following function can change the margin/padding of the text in UITextView

public func UIEdgeInsetsMake(_ top: CGFloat, _ left: CGFloat, _ bottom: CGFloat, _ right: CGFloat) -> UIEdgeInsets

so in this case is

 self.textView?.textContainerInset = UIEdgeInsetsMake(0, 0, 0, 0)

For iOS 7.0, I've found that the contentInset trick no longer works. This is the code I used to get rid of the margin/padding in iOS 7.

This brings the left edge of the text to the left edge of the container:

textView.textContainer.lineFragmentPadding = 0

This causes the top of the text to align with the top of the container

textView.textContainerInset = .zero

Both Lines are needed to completely remove the margin/padding.


For iOS 10, the following line works for the top and bottom padding removing.

captionTextView.textContainerInset = UIEdgeInsetsMake(0, 0, 0, 0)

For me (iOS 11 & Xcode 9.4.1) what worked magically was setting up textView.font property to UIFont.preferred(forTextStyle:UIFontTextStyle) style and also the first answer as mentioned by @Fattie. But the @Fattie answer did not work till I set the textView.font property else UITextView keeps behaving erratically.


For SwiftUI

If you are making your own TextView using UIViewRepresentable and want to control the padding, in your makeUIView function, simply do:

uiTextView.textContainerInset = UIEdgeInsets(top: 10, left: 18, bottom: 0, right: 18)

or whatever you want.


Latest Swift:

self.textView.textContainerInset = .init(top: -2, left: 0, bottom: 0, right: 0)
self.textView.textContainer.lineFragmentPadding = 0

you can use textContainerInset property of UITextView:

textView.textContainerInset = UIEdgeInsetsMake(10, 10, 10, 10);

(top, left, bottom, right)


The textView scrolling also affect the position of the text and make it look like not vertically centered. I managed to center the text in the view by disabling the scrolling and setting the top inset to 0:

    textView.scrollEnabled = NO;
    textView.textContainerInset = UIEdgeInsetsMake(0, textView.textContainerInset.left, textView.textContainerInset.bottom, textView.textContainerInset.right);

For some reason I haven't figured it out yet, the cursor is still not centered before the typing begins, but the text centers immediately as I start typing.


All these answers address the title question, but I wanted to propose some solutions for the problems presented in the body of the OP's question.

Size of Text Content

A quick way to calculate the size of the text inside the UITextView is to use the NSLayoutManager:

UITextView *textView;
CGSize textSize = [textView usedRectForTextContainer:textView.textContainer].size;

This gives the total scrollable content, which may be bigger than the UITextView's frame. I found this to be much more accurate than textView.contentSize since it actually calculates how much space the text takes up. For example, given an empty UITextView:

textView.frame.size = (width=246, height=50)
textSize = (width=10, height=16.701999999999998)
textView.contentSize = (width=246, height=33)
textView.textContainerInset = (top=8, left=0, bottom=8, right=0)

Line Height

UIFont has a property that quickly allows you to get the line height for the given font. So you can quickly find the line height of the text in your UITextView with:

UITextView *textView;
CGFloat lineHeight = textView.font.lineHeight;

Calculating Visible Text Size

Determining the amount of text that is actually visible is important for handling a "paging" effect. UITextView has a property called textContainerInset which actually is a margin between the actual UITextView.frame and the text itself. To calculate the real height of the visible frame you can perform the following calculations:

UITextView *textView;
CGFloat textViewHeight = textView.frame.size.height;
UIEdgeInsets textInsets = textView.textContainerInset;
CGFloat textHeight = textViewHeight - textInsets.top - textInsets.bottom;

Determining Paging Size

Lastly, now that you have the visible text size and the content, you can quickly determine what your offsets should be by subtracting the textHeight from the textSize:

// where n is the page number you want
CGFloat pageOffsetY = textSize - textHeight * (n - 1);
textView.contentOffset = CGPointMake(textView.contentOffset.x, pageOffsetY);

// examples
CGFloat page1Offset = 0;
CGFloat page2Offset = textSize - textHeight
CGFloat page3Offset = textSize - textHeight * 2

Using all of these methods, I didn't touch my insets and I was able to go to the caret or wherever in the text that I want.


I have found one more approach, getting view with text from UITextView's subviews and setting it up in layoutSubview method of a subclass:

- (void)layoutSubviews {
    [super layoutSubviews];

    const int textViewIndex = 1;
    UIView *textView = [self.subviews objectAtIndex:textViewIndex];
    textView.frame = CGRectMake(
                                 kStatusViewContentOffset,
                                 0.0f,
                                 self.bounds.size.width - (2.0f * kStatusViewContentOffset),
                                 self.bounds.size.height - kStatusViewContentOffset);
}

I would definitely avoid any answers involving hard-coded values, as the actual margins may change with user font-size settings, etc.

Here is @user1687195's answer, written without modifying the textContainer.lineFragmentPadding (because the docs state this is not the intended usage).

This works great for iOS 7 and later.

self.textView.textContainerInset = UIEdgeInsetsMake(
                                      0, 
                                      -self.textView.textContainer.lineFragmentPadding, 
                                      0, 
                                      -self.textView.textContainer.lineFragmentPadding);

This is effectively the same outcome, just a bit cleaner in that it doesn't misuse the lineFragmentPadding property.


In case anyone looking for latest Swift version then below code is working fine with Xcode 10.2 and Swift 4.2

yourTextView.textContainerInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)

Here is an updated version of Fattie's very helpful answer. It adds 2 important lines that helped me get the layout working on iOS 10 and 11 (and probably on lower ones, too):

@IBDesignable class UITextViewFixed: UITextView {
    override func layoutSubviews() {
        super.layoutSubviews()
        setup()
    }
    func setup() {
        translatesAutoresizingMaskIntoConstraints = true
        textContainerInset = UIEdgeInsets.zero
        textContainer.lineFragmentPadding = 0
        translatesAutoresizingMaskIntoConstraints = false
    }
}

The important lines are the two translatesAutoresizingMaskIntoConstraints = <true/false> statements!

This surprisingly removes all margins in all my circumstances!

While the textView is not the first responder it could happen that there is some strange bottom margin that could not be solved using the sizeThatFits method that is mentioned in the accepted answer.

When tapping into the textView suddenly the strange bottom margin disappeared and everything looked like it should, but only as soon as the textView has got firstResponder.

So I read here on SO that enabling and disabling translatesAutoresizingMaskIntoConstraints does help when setting the frame/bounds manually in between the calls.

Fortunately this not only works with frame setting but with the 2 lines of setup() sandwiched between the two translatesAutoresizingMaskIntoConstraints calls!

This for example is very helpful when calculating the frame of a view using systemLayoutSizeFitting on a UIView. Gives back the correct size (which previously it didn't)!

As in the original answer mentioned:
Don't forget to turn off scrollEnabled in the Inspector!
That solution does work properly in storyboard, as well as at runtime.

That's it, now you're really done!


In case you want to set a HTML string and avoid the bottom padding, please make sure that you are not using block tags i.e. div, p.

In my case this was the reason. You can easily test it out by replacing occurrences of block tags with i.e. span tag.


Building off some of the good answers already given, here is a purely Storyboard / Interface Builder-based solution that works in iOS 7.0+

Set the UITextView's User Defined Runtime Attributes for the following keys:

textContainer.lineFragmentPadding
textContainerInset

Interface Builder


Examples related to ios

Adding a UISegmentedControl to UITableView Crop image to specified size and picture location Undefined Symbols error when integrating Apptentive iOS SDK via Cocoapods Keep placeholder text in UITextField on input in IOS Accessing AppDelegate from framework? Autoresize View When SubViews are Added Warp \ bend effect on a UIView? Speech input for visually impaired users without the need to tap the screen make UITableViewCell selectable only while editing Xcode 12, building for iOS Simulator, but linking in object file built for iOS, for architecture arm64

Examples related to iphone

Detect if the device is iPhone X Xcode 8 shows error that provisioning profile doesn't include signing certificate Access files in /var/mobile/Containers/Data/Application without jailbreaking iPhone Certificate has either expired or has been revoked Missing Compliance in Status when I add built for internal testing in Test Flight.How to solve? cordova run with ios error .. Error code 65 for command: xcodebuild with args: "Could not find Developer Disk Image" Reason: no suitable image found iPad Multitasking support requires these orientations How to insert new cell into UITableView in Swift

Examples related to cocoa-touch

Include of non-modular header inside framework module Move textfield when keyboard appears swift Create space at the beginning of a UITextField Navigation bar with UIImage for title Generate a UUID on iOS from Swift How do I write a custom init for a UIView subclass in Swift? creating custom tableview cells in swift How would I create a UIAlertView in Swift? Get current NSDate in timestamp format How do you add an in-app purchase to an iOS application?

Examples related to uikit

CGRectMake, CGPointMake, CGSizeMake, CGRectZero, CGPointZero is unavailable in Swift How to trap on UIViewAlertForUnsatisfiableConstraints? Changing Placeholder Text Color with Swift Move view with keyboard using Swift How to use UIVisualEffectView to Blur Image? preferredStatusBarStyle isn't called How to change Navigation Bar color in iOS 7? UICollectionView spacing margins How to programmatically get iOS status bar height Get UIScrollView to scroll to the top

Examples related to uitextview

how to make UITextView height dynamic according to text length? Swift: Display HTML data in a label or textView Add placeholder text inside UITextView in Swift? How can I make a clickable link in an NSAttributedString? UITextView that expands to text using auto layout Change UITextField and UITextView Cursor / Caret Color Display html text in uitextview How to style UITextview to like Rounded Rect text field? How to create a multiline UITextfield? Placeholder in UITextView