4

I am trying to create a read more button at the end of my label. I want it to display 3 lines by default. I am coding in swift not objective c. Only when the user clicks the read more part of the label, should the label expand. It should look and work exactly like it does on instagram except on Instagram, it is in a tableview cell. My label and read more button will be in a scrollview. I have managed to get the expanding and contracting part working by adjusting the number of lines property of the label.

if descriptionLabel.numberOfLines == 0{
    descriptionLabel.numberOfLines = 3
}else {
    descriptionLabel.numberOfLines = 0
}
descriptionLabel.lineBreakMode = NSLineBreakMode.byWordWrapping

I am having problems with putting a "...more" at the end of the label and cutting the text off at the right place. I have looked at other people's responses to similar questions but nothing seems to work properly.

I can put a button over the last line of text so making the see more part of the label clickable also isn't the problem. The problem I am having is truncating the text at the right place and placing the see more text at the right place so that it displays.

I also want the read more button to only appear when it is necessary. I don't want to it appear when there are only 1-3 lines of text. This is also something I am having issues with.

I can't use this https://github.com/apploft/ExpandableLabel because it does not support scrollviews just tableviews.

the swift solution here didn't work: Add "...Read More" to the end of UILabel. It crashed the app.

Finally, the read more button should be in line with the last line of text and at the end of it. It would be an added benefit it this worked in a tableview cell as well!

Nevin Jethmalani
  • 2,726
  • 4
  • 23
  • 58
  • Did you examine the "ExpandableLabel" GitHub project you linked to, to see if you *could* use it outside of a table view? I took a quick look, and it appears it will do exactly what you want - the author has simply shown a demo of using it in a table view cell (which would be a very common use case). I'd suggest you give it a try. – DonMag Sep 28 '17 at 18:59
  • I did give it a try and in iOS 10 it worked perfectly but in iOS 11 it doesn’t work – Nevin Jethmalani Sep 28 '17 at 19:00
  • *"... in iOS 11 it doesn’t work ..."* Can you be a little more vague? What doesn't work? Errors? Warnings? Nothing shows up? Label shows but can't be tapped? Label shows but won't collapse? – DonMag Sep 28 '17 at 19:11
  • Works fine in a scroll view for me on iOS 10 (I don't have iOS 11 available here). Did you get it working in 10, but it "doesn't wok" in 11? – DonMag Sep 28 '17 at 19:17
  • Yes it does not work in iOS 11. The see more button just does not show up. No errors in the console or crashes. It just does not show up. – Nevin Jethmalani Sep 28 '17 at 19:38
  • But you got it working in iOS 10? And the exact same code - just running on iOS 11 - the button doesn't show up? Have you gone into Debug View Hierarchy to see what's up? – DonMag Sep 28 '17 at 19:42
  • Yes it works perfectly in iOS 10 – Nevin Jethmalani Sep 28 '17 at 19:43
  • Curious... Loaded my sample project on a system with Xcode 9 and iOS 11... no problems getting this "ExpandableLabel" to work embedded in a `UIScrollView`. I simply forked the original GitHub repo and added a `UIViewController` with a scroll view that contains a couple static labels + a `ExpandableLabel`. You can take a look at it here, if you want: https://github.com/DonMag/ExpandableLabel – DonMag Oct 01 '17 at 16:01

2 Answers2

1

I found ReadMoreTextView in Github, which is based on UITextView. The key method in this library is the following:

private func characterIndexBeforeTrim(range rangeThatFits: NSRange) -> Int {
    if let text = attributedReadMoreText {
        let readMoreBoundingRect = attributedReadMoreText(text: text, boundingRectThatFits: textContainer.size)
        let lastCharacterRect = layoutManager.boundingRectForCharacterRange(range: NSMakeRange(NSMaxRange(rangeThatFits)-1, 1), inTextContainer: textContainer)
        var point = lastCharacterRect.origin
        point.x = textContainer.size.width - ceil(readMoreBoundingRect.size.width)
        let glyphIndex = layoutManager.glyphIndex(for: point, in: textContainer, fractionOfDistanceThroughGlyph: nil)
        let characterIndex = layoutManager.characterIndexForGlyph(at: glyphIndex)
        return characterIndex - 1
    } else {
        return NSMaxRange(rangeThatFits) - readMoreText!.length
    }
}

To display text like "xxxx...Read More", the library

  1. Get how many characters could be display in the UITextView: Use NSLayoutManager.characterRange(forGlyphRange:, actualGlyphRange:)
  2. Get the position of the last visible character and the width of "...Read More": Use NSLayoutManager.boundingRect(forGlyphRange glyphRange: NSRange, in container: NSTextContainer)
  3. Get the character index before trimming: Use NSLayoutManager.characterIndexForGlyph(at glyphIndex: Int)
  4. Replace text which should be trimmed with "...Read More": UITextStorage.replaceCharacters(in range: NSRange, with attrString: NSAttributedString)
Steven
  • 99
  • 5
-1

Please check :

func addSeeMore(str: String, maxLength: Int) -> NSAttributedString {
    var attributedString = NSAttributedString()
    let index: String.Index = str.characters.index(str.startIndex, offsetBy: maxLength)
    let editedText = String(str.prefix(upTo: index)) + "... See More"
    attributedString = NSAttributedString(string: editedText)

    return attributedString
}

You can use like :

let str = "Lorem Ipsum is simply dummy text of the printing and typesetting industry."
descriptionLabel.attributedText = addSeeMore(str: str, maxLength: 20) 
// Output : Lorem Ipsum is simpl... See More
Vini App
  • 7,339
  • 2
  • 26
  • 43