简体   繁体   中英

Display partial lines of text on a deliberately truncated UITextView

There's many many questions on the internet complaining about how UITextView cuts off text prematurely, but I can't find any about the reverse situation where you actually want it to cut off the text before the end - but part way through a line.

It appears that UITextView will only ever render discreet lines of text - ie if it's not big enough, it will not render beyond the last line that fully fits inside it. I am seeking a way to make it render a partial line of text right up to the bottom boundary.

ie it currently does what's on the left here, whereas I am looking for a way to do what's on the right:

部分文本行的插图

My UITextView has a fixed height and scrolling is disabled. I am assigning attributedText that could have bold, italics, etc rather than a simple string.

I don't think you can accomplish this with properties of UITextView .

One approach, though:

  • embed the text view in a "container" UIView
  • constrain the height of the container view as desired
  • set Clips to Bounds of the container view to TRUE

This will allow the text view to grow vertically to fit its text, but the container view will clip the portion you don't want visible.

Container view background is yellow, text view background is cyan:

在此处输入图片说明

Using Debug View Hierarchy - Show Clipped Content :

在此处输入图片说明

and here's the 3D view:

在此处输入图片说明


EDIT

Here is another approach -- may or may not work for your requirements...

  • UITextView - set position and width and height constraints
  • NOT editable
  • YES selectable
  • Scrolling Enabled - this will show the partial line
  • DISable show scroll indicators

Now, add a UIPanGestureRecognizer to that text view. It doesn't have to do anything... but it will (at least it appears to) prevent the text view from receiving the pan gesture, and thus prevents scrolling.

One noticeable issue though... if you have enough text that you get multiple lines, when you start selecting you can drag the selection handle down to the next line(s) and the text will scroll up... could probably prevent that in code, or maybe that would be desirable and then reset the scroll after the user has copied the selected text.

OK, so it got ugly pretty fast, but I did manage to do this...

Rather than using a standard UITextView created through Interface Builder, I had to manually build one using a custom NSTextContainer :

let frame = containerView.frame

let textStorage = NSTextStorage(string: "")
let layoutManager = NSLayoutManager()

let textContainer = CardTextContainer(size: CGSize(width: frame.size.width, height: CGFloat.greatestFiniteMagnitude))
textContainer.widthTracksTextView = true

layoutManager.addTextContainer(textContainer)
textStorage.addLayoutManager(layoutManager)

let textView = UITextView(frame: frame, textContainer: textContainer)
textView.isScrollEnabled = false

containerView.addSubview(textView)

I then defined CardTextContainer as a subclass of NSTextContainer that delegates its behaviour to the base class, except in the scenario where the line being rendered overlaps with the bottom of the NSTextView :

class CardTextContainer : NSTextContainer {

    override func lineFragmentRect(forProposedRect proposedRect: CGRect, at characterIndex: Int, writingDirection baseWritingDirection: NSWritingDirection, remaining remainingRect: UnsafeMutablePointer<CGRect>?) -> CGRect {

        let fragmentRect = super.lineFragmentRect(forProposedRect: proposedRect, at: characterIndex, writingDirection: baseWritingDirection, remaining: remainingRect)

        if ((fragmentRect.size.width > 0) || (fragmentRect.size.height > 0)) {
            // Just rely on the base implementation when it's working well
            return fragmentRect
        } else {
            // See if we could do slightly better than they did
            if (proposedRect.origin.y < self.size.height) {
                return CGRect(origin: proposedRect.origin, size: CGSize(width: self.size.width, height: proposedRect.size.height))
            } else {
                // Fall back to the 'I give up' solution
                return fragmentRect
            }
        }        
    }    
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM