在UILabel的末尾添加"... Read More"

Yos*_*far 12 objective-c nsattributedstring uilabel ios

我有一个UILabel,在某些情况下文本比UILabel自身长,所以我看到文本,因为"bla bla bla..."我想...Read More在结尾添加一个按钮文本UILabel..

我已经阅读了一些帖子,但是他们提供的解决方案对我来说并不好,例如:计算将输入多少个字符UILabel,但是我使用的字体每个字符都有不同的宽度.

我怎么能设法做到这一点?

提前致谢!

ram*_*a n 23

Swift4(IOS 11.2)

Readmore在标签中没有行动结束

extension UILabel {

    func addTrailing(with trailingText: String, moreText: String, moreTextFont: UIFont, moreTextColor: UIColor) {
        let readMoreText: String = trailingText + moreText

        let lengthForVisibleString: Int = self.visibleTextLength
        let mutableString: String = self.text!
        let trimmedString: String? = (mutableString as NSString).replacingCharacters(in: NSRange(location: lengthForVisibleString, length: ((self.text?.count)! - lengthForVisibleString)), with: "")
        let readMoreLength: Int = (readMoreText.count)
        let trimmedForReadMore: String = (trimmedString! as NSString).replacingCharacters(in: NSRange(location: ((trimmedString?.count ?? 0) - readMoreLength), length: readMoreLength), with: "") + trailingText
        let answerAttributed = NSMutableAttributedString(string: trimmedForReadMore, attributes: [NSAttributedStringKey.font: self.font])
        let readMoreAttributed = NSMutableAttributedString(string: moreText, attributes: [NSAttributedStringKey.font: moreTextFont, NSAttributedStringKey.foregroundColor: moreTextColor])
        answerAttributed.append(readMoreAttributed)
        self.attributedText = answerAttributed
    }

    var visibleTextLength: Int {
        let font: UIFont = self.font
        let mode: NSLineBreakMode = self.lineBreakMode
        let labelWidth: CGFloat = self.frame.size.width
        let labelHeight: CGFloat = self.frame.size.height
        let sizeConstraint = CGSize(width: labelWidth, height: CGFloat.greatestFiniteMagnitude)

        let attributes: [AnyHashable: Any] = [NSAttributedStringKey.font: font]
        let attributedText = NSAttributedString(string: self.text!, attributes: attributes as? [NSAttributedStringKey : Any])
        let boundingRect: CGRect = attributedText.boundingRect(with: sizeConstraint, options: .usesLineFragmentOrigin, context: nil)

        if boundingRect.size.height > labelHeight {
            var index: Int = 0
            var prev: Int = 0
            let characterSet = CharacterSet.whitespacesAndNewlines
            repeat {
                prev = index
                if mode == NSLineBreakMode.byCharWrapping {
                    index += 1
                } else {
                    index = (self.text! as NSString).rangeOfCharacter(from: characterSet, options: [], range: NSRange(location: index + 1, length: self.text!.count - index - 1)).location
                }
            } while index != NSNotFound && index < self.text!.count && (self.text! as NSString).substring(to: index).boundingRect(with: sizeConstraint, options: .usesLineFragmentOrigin, attributes: attributes as? [NSAttributedStringKey : Any], context: nil).size.height <= labelHeight
            return prev
        }
        return self.text!.count
    }
}
Run Code Online (Sandbox Code Playgroud)

Swift 4.2

extension UILabel {

        func addTrailing(with trailingText: String, moreText: String, moreTextFont: UIFont, moreTextColor: UIColor) {
            let readMoreText: String = trailingText + moreText

            let lengthForVisibleString: Int = self.vissibleTextLength
            let mutableString: String = self.text!
            let trimmedString: String? = (mutableString as NSString).replacingCharacters(in: NSRange(location: lengthForVisibleString, length: ((self.text?.count)! - lengthForVisibleString)), with: "")
            let readMoreLength: Int = (readMoreText.count)
            let trimmedForReadMore: String = (trimmedString! as NSString).replacingCharacters(in: NSRange(location: ((trimmedString?.count ?? 0) - readMoreLength), length: readMoreLength), with: "") + trailingText
            let answerAttributed = NSMutableAttributedString(string: trimmedForReadMore, attributes: [NSAttributedString.Key.font: self.font])
            let readMoreAttributed = NSMutableAttributedString(string: moreText, attributes: [NSAttributedString.Key.font: moreTextFont, NSAttributedString.Key.foregroundColor: moreTextColor])
            answerAttributed.append(readMoreAttributed)
            self.attributedText = answerAttributed
        }

        var vissibleTextLength: Int {
            let font: UIFont = self.font
            let mode: NSLineBreakMode = self.lineBreakMode
            let labelWidth: CGFloat = self.frame.size.width
            let labelHeight: CGFloat = self.frame.size.height
            let sizeConstraint = CGSize(width: labelWidth, height: CGFloat.greatestFiniteMagnitude)

            let attributes: [AnyHashable: Any] = [NSAttributedString.Key.font: font]
            let attributedText = NSAttributedString(string: self.text!, attributes: attributes as? [NSAttributedString.Key : Any])
            let boundingRect: CGRect = attributedText.boundingRect(with: sizeConstraint, options: .usesLineFragmentOrigin, context: nil)

            if boundingRect.size.height > labelHeight {
                var index: Int = 0
                var prev: Int = 0
                let characterSet = CharacterSet.whitespacesAndNewlines
                repeat {
                    prev = index
                    if mode == NSLineBreakMode.byCharWrapping {
                        index += 1
                    } else {
                        index = (self.text! as NSString).rangeOfCharacter(from: characterSet, options: [], range: NSRange(location: index + 1, length: self.text!.count - index - 1)).location
                    }
                } while index != NSNotFound && index < self.text!.count && (self.text! as NSString).substring(to: index).boundingRect(with: sizeConstraint, options: .usesLineFragmentOrigin, attributes: attributes as? [NSAttributedString.Key : Any], context: nil).size.height <= labelHeight
                return prev
            }
            return self.text!.count
        }
    }
Run Code Online (Sandbox Code Playgroud)

用法

let readmoreFont = UIFont(name: "Helvetica-Oblique", size: 11.0)
let readmoreFontColor = UIColor.blue
DispatchQueue.main.async {
    self.yourLabel.addTrailing(with: "... ", moreText: "Readmore", moreTextFont: readmoreFont!, moreTextColor: readmoreFontColor)
}
Run Code Online (Sandbox Code Playgroud)

结果

Readmore标签输出

注意: - Readmore不包括动作

  • 我们如何添加动作? (4认同)
  • @JesseOnolememen您也可以在标签上添加同样有效的操作 (2认同)

Yos*_*far 10

所以我这样做是为了添加Read More ...按钮UITextView,UITextField或者UILabel:

- (void)addReadMoreStringToUILabel:(UILabel*)label
{
    NSString *readMoreText = @" ...Read More";
    NSInteger lengthForString = label.text.length;
    if (lengthForString >= 30)
    {
        NSInteger lengthForVisibleString = [self fitString:label.text intoLabel:label];
        NSMutableString *mutableString = [[NSMutableString alloc] initWithString:label.text];
        NSString *trimmedString = [mutableString stringByReplacingCharactersInRange:NSMakeRange(lengthForVisibleString, (label.text.length - lengthForVisibleString)) withString:@""];
        NSInteger readMoreLength = readMoreText.length;
        NSString *trimmedForReadMore = [trimmedString stringByReplacingCharactersInRange:NSMakeRange((trimmedString.length - readMoreLength), readMoreLength) withString:@""];
        NSMutableAttributedString *answerAttributed = [[NSMutableAttributedString alloc] initWithString:trimmedForReadMore attributes:@{
                                                                                                                                        NSFontAttributeName : label.font
                                                                                                                                        }];

        NSMutableAttributedString *readMoreAttributed = [[NSMutableAttributedString alloc] initWithString:readMoreText attributes:@{
                                                                                                                                        NSFontAttributeName : Font(TWRegular, 12.),
                                                                                                                                        NSForegroundColorAttributeName : White
                                                                                                                                        }];

        [answerAttributed appendAttributedString:readMoreAttributed];
        label.attributedText = answerAttributed;

        UITagTapGestureRecognizer *readMoreGesture = [[UITagTapGestureRecognizer alloc] initWithTarget:self action:@selector(readMoreDidClickedGesture:)];
        readMoreGesture.tag = 1;
        readMoreGesture.numberOfTapsRequired = 1;
        [label addGestureRecognizer:readMoreGesture];

        label.userInteractionEnabled = YES;
    }
    else {

        NSLog(@"No need for 'Read More'...");

    }
}
Run Code Online (Sandbox Code Playgroud)

fitString:intoLabel可以在这里找到使用方法.

至于UITagTapGestureRecognizer它只是一个UITapGestureRecognizer带有NSInteger名为tag 的属性的普通子类.我这样做是因为我想确定哪些Read More...被点击了我的情况下我有不止一个UIViewController.你可以正常使用UITapGestureRecognizer.

请享用!


Avn*_*arr 8

Tttattributed标签具有此功能

https://github.com/TTTAttributedLabel/TTTAttributedLabel

您需要将"截断"标记设置为"阅读更多..."

看到

attributedTruncationToken

var subTitleLabel = TTTAttributedLabel(frame : frame)
    self.addSubview(subTitleLabel)
    var trunc = NSMutableAttributedString(string: "...more")
    trunc.addAttribute(NSFontAttributeName, value: UIFont.systemFontOfSize(12), range: NSMakeRange(0, 7))
    trunc.addAttribute(NSForegroundColorAttributeName, value: UIColor.blueColor(), range: NSMakeRange(0, 7))
    subTitleLabel.attributedTruncationToken = trunc
    subTitleLabel.numberOfLines = 1
    subTitleLabel.autoresizingMask = UIViewAutoresizing.FlexibleHeight | UIViewAutoresizing.FlexibleWidth
Run Code Online (Sandbox Code Playgroud)


Lan*_*ria 8

这适用于 Swift 5

这是@ramchandran 答案的更安全版本,因为您不知道用户将输入多少个字符。

在他的回答中,如果用户输入的字符串长度小于您决定使用的任何文本的长度,... Readmore那么它将崩溃。例如,这就是您使用它的方式

if yourLabel.text!.count > 1 {

   let readmoreFont = UIFont(name: "Helvetica-Oblique", size: 11.0)
    let readmoreFontColor = UIColor.blue
    DispatchQueue.main.async {
        self.yourLabel.addTrailing(with: "... ", moreText: "Readmore", moreTextFont: readmoreFont!, moreTextColor: readmoreFontColor)
    }
}
Run Code Online (Sandbox Code Playgroud)

在上面的例子中,输出... Readmore总共是 12 个字符。如果用户输入的是字符串,yourLabel.text = "12345678"则字符串的文本将只有 8 个字符。它会崩溃,因为在((trimmedString?.count ?? 0) - readMoreLength)下面的行中使用的范围会产生负面结果:

// “12345678” minus “... Readmore” = negative four (8 - 12 = -4)
let trimmedForReadMore: String = (trimmedString! as NSString).replacingCharacters(in: NSRange(location: ((trimmedString?.count ?? 0) - readMoreLength), length: readMoreLength), with: "") + trailingText
Run Code Online (Sandbox Code Playgroud)

我添加了一个安全检查,以确保输入的字符串小于或等于您决定使用的任何字符数,因为... Readmore它将返回并且永远不会到达导致崩溃的行:

// trimmedString is the string the user entered
guard let safeTrimmedString = trimmedString else { return }
if safeTrimmedString.count <= readMoreLength { return }
Run Code Online (Sandbox Code Playgroud)

它位于addTrailing函数的中心

extension UILabel{    
    
    func addTrailing(with trailingText: String, moreText: String, moreTextFont: UIFont, moreTextColor: UIColor) {
        
        let readMoreText: String = trailingText + moreText
        
        if self.visibleTextLength == 0 { return }
        
        let lengthForVisibleString: Int = self.visibleTextLength
        
        if let myText = self.text {
            
            let mutableString: String = myText
            
            let trimmedString: String? = (mutableString as NSString).replacingCharacters(in: NSRange(location: lengthForVisibleString, length: myText.count - lengthForVisibleString), with: "")
            
            let readMoreLength: Int = (readMoreText.count)
            
            guard let safeTrimmedString = trimmedString else { return }
            
            if safeTrimmedString.count <= readMoreLength { return }
            
            print("this number \(safeTrimmedString.count) should never be less\n")
            print("then this number \(readMoreLength)")
            
            // "safeTrimmedString.count - readMoreLength" should never be less then the readMoreLength because it'll be a negative value and will crash
            let trimmedForReadMore: String = (safeTrimmedString as NSString).replacingCharacters(in: NSRange(location: safeTrimmedString.count - readMoreLength, length: readMoreLength), with: "") + trailingText
            
            let answerAttributed = NSMutableAttributedString(string: trimmedForReadMore, attributes: [NSAttributedString.Key.font: self.font])
            let readMoreAttributed = NSMutableAttributedString(string: moreText, attributes: [NSAttributedString.Key.font: moreTextFont, NSAttributedString.Key.foregroundColor: moreTextColor])
            answerAttributed.append(readMoreAttributed)
            self.attributedText = answerAttributed
        }
    }
    
    var visibleTextLength: Int {
        
        let font: UIFont = self.font
        let mode: NSLineBreakMode = self.lineBreakMode
        let labelWidth: CGFloat = self.frame.size.width
        let labelHeight: CGFloat = self.frame.size.height
        let sizeConstraint = CGSize(width: labelWidth, height: CGFloat.greatestFiniteMagnitude)
        
        if let myText = self.text {
            
            let attributes: [AnyHashable: Any] = [NSAttributedString.Key.font: font]
            let attributedText = NSAttributedString(string: myText, attributes: attributes as? [NSAttributedString.Key : Any])
            let boundingRect: CGRect = attributedText.boundingRect(with: sizeConstraint, options: .usesLineFragmentOrigin, context: nil)
            
            if boundingRect.size.height > labelHeight {
                var index: Int = 0
                var prev: Int = 0
                let characterSet = CharacterSet.whitespacesAndNewlines
                repeat {
                    prev = index
                    if mode == NSLineBreakMode.byCharWrapping {
                        index += 1
                    } else {
                        index = (myText as NSString).rangeOfCharacter(from: characterSet, options: [], range: NSRange(location: index + 1, length: myText.count - index - 1)).location
                    }
                } while index != NSNotFound && index < myText.count && (myText as NSString).substring(to: index).boundingRect(with: sizeConstraint, options: .usesLineFragmentOrigin, attributes: attributes as? [NSAttributedString.Key : Any], context: nil).size.height <= labelHeight
                return prev
            }
        }
        
        if self.text == nil {
            return 0
        } else {
            return self.text!.count
        }
    }
}
Run Code Online (Sandbox Code Playgroud)


Gur*_*ngh 6

斯威夫特 4 和斯威夫特 5。我需要实现相同的。由于已经给出了答案,但据我所知 TTTAttributedLabel 是最好的方法。它使您可以更好地控制内容。易于查找地址、链接、日期等。您还可以更改链接的颜色。上面的答案中已经给出了 TTTAttributedLabel 库链接。让我们来实施。

let kCharacterBeforReadMore =  20
let kReadMoreText           =  "...ReadMore"
let kReadLessText           =  "...ReadLess"

@IBOutlet weak var labelText: TTTAttributedLabel! // setYouLabel Class to TTTAttributedLabel in StoryBoard
var strFull = ""

 override func viewDidLoad() {
      super.viewDidLoad()
      strFull = "I need to implement the same. As answers are already given but according to me TTTAttributedLabel is the best way to do it. I gives I need to implement the same. As answers are already given but according to me TTTAttributedLabel is the best way to do it. I gives you"
      labelText.showTextOnTTTAttributeLable(str: strFull, readMoreText: kReadMoreText, readLessText: kReadLessText, font: UIFont.init(name: "Helvetica-Bold", size: 24.0)!, charatersBeforeReadMore: kCharacterBeforReadMore, activeLinkColor: UIColor.blue, isReadMoreTapped: false, isReadLessTapped: false)
      labelText.delegate = self
   }
      func readMore(readMore: Bool) {
        labelText.showTextOnTTTAttributeLable(str: strFull, readMoreText: kReadMoreText, readLessText: kReadLessText, font: nil, charatersBeforeReadMore: kCharacterBeforReadMore, activeLinkColor: UIColor.blue, isReadMoreTapped: readMore, isReadLessTapped: false)
      }
      func readLess(readLess: Bool) {
        labelText.showTextOnTTTAttributeLable(str: strFull, readMoreText: kReadMoreText, readLessText: kReadLessText, font: nil, charatersBeforeReadMore: kCharacterBeforReadMore, activeLinkColor: UIColor.blue, isReadMoreTapped: readLess, isReadLessTapped: true)
      }
}
Run Code Online (Sandbox Code Playgroud)

在这里,我创建了 TTTAttributedLabel 的扩展,并将 ReadMore 和 ReadLess 逻辑放在这里。可以根据你的修改。

 extension TTTAttributedLabel {
      func showTextOnTTTAttributeLable(str: String, readMoreText: String, readLessText: String, font: UIFont?, charatersBeforeReadMore: Int, activeLinkColor: UIColor, isReadMoreTapped: Bool, isReadLessTapped: Bool) {

        let text = str + readLessText
        let attributedFullText = NSMutableAttributedString.init(string: text)
        let rangeLess = NSString(string: text).range(of: readLessText, options: String.CompareOptions.caseInsensitive)
//Swift 5
       // attributedFullText.addAttributes([NSAttributedStringKey.foregroundColor : UIColor.blue], range: rangeLess)
        attributedFullText.addAttributes([NSAttributedString.Key.foregroundColor : UIColor.blue], range: rangeLess)

        var subStringWithReadMore = ""
        if text.count > charatersBeforeReadMore {
          let start = String.Index(encodedOffset: 0)
          let end = String.Index(encodedOffset: charatersBeforeReadMore)
          subStringWithReadMore = String(text[start..<end]) + readMoreText
        }

        let attributedLessText = NSMutableAttributedString.init(string: subStringWithReadMore)
        let nsRange = NSString(string: subStringWithReadMore).range(of: readMoreText, options: String.CompareOptions.caseInsensitive)
        //Swift 5
       // attributedLessText.addAttributes([NSAttributedStringKey.foregroundColor : UIColor.blue], range: nsRange)
        attributedLessText.addAttributes([NSAttributedString.Key.foregroundColor : UIColor.blue], range: nsRange)
      //  if let _ = font {// set font to attributes
      //   self.font = font
      //  }
        self.attributedText = attributedLessText
        self.activeLinkAttributes = [NSAttributedString.Key.foregroundColor : UIColor.blue]
        //Swift 5
       // self.linkAttributes = [NSAttributedStringKey.foregroundColor : UIColor.blue]
        self.linkAttributes = [NSAttributedString.Key.foregroundColor : UIColor.blue]
        self.addLink(toTransitInformation: ["ReadMore":"1"], with: nsRange)

        if isReadMoreTapped {
          self.numberOfLines = 0
          self.attributedText = attributedFullText
          self.addLink(toTransitInformation: ["ReadLess": "1"], with: rangeLess)
        }
        if isReadLessTapped {
          self.numberOfLines = 3
          self.attributedText = attributedLessText
        }
      }
    }
Run Code Online (Sandbox Code Playgroud)

您需要实现 TTTAttributedLabel 的 didSelectLinkWithTransitInformation 委托。在这里你可以得到你已经通过的组件

extension ViewController: TTTAttributedLabelDelegate {
  func attributedLabel(_ label: TTTAttributedLabel!, didSelectLinkWithTransitInformation components: [AnyHashable : Any]!) {
    if let _ = components as? [String: String] {
      if let value = components["ReadMore"] as? String, value == "1" {
        self.readMore(readMore: true)
      }
      if let value = components["ReadLess"] as? String, value == "1" {
        self.readLess(readLess: true)
      }
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

结果-点击之前阅读更多

在此处输入图片说明

结果 - 点击后阅读更多

在此处输入图片说明