Добавьте "... Подробнее" в конец UILabel

У меня есть UILabel, и в некоторых случаях текст длиннее, чем сам UILabel, поэтому я вижу текст как "bla bla bla..." Я хочу добавить текст кнопки ...Read More в конце UILabel..

Я прочитал несколько сообщений, но они предлагают решения, которые мне не подходят, например: чтобы подсчитать, сколько символов войдет в UILabel, но с шрифтом, который я использую, каждый символ имеет другую ширину.

Как я могу это сделать?

Спасибо заранее!

Ответ 1

Так вот что я сделал, чтобы добавить кнопку Подробнее... в 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'...");

    }
}

Существует метод fitString:intoLabel, который можно найти здесь.

Что касается UITagTapGestureRecognizer, это обычный нормальный подкласс UITapGestureRecognizer с свойством NSInteger, называемым тегом. Я сделал это, потому что я хочу определить, какие Read More... были нажаты в я случае. У меня более одного в одном и том же UIViewController. Вы можете использовать обычный UITapGestureRecognizer.

Наслаждайтесь!

Ответ 2

Swift4 (IOS 11.2)

Читайте дальше в конце этикетки без действий

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
    }
}

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
        }
    }

использование

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)
}

Результат

Readmore label output

ПРИМЕЧАНИЕ: - Действие не включено для Readmore

Ответ 3

Tttattributed label имеет эту функцию

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

Ответ 4

Это работает для Swift 4.2

Вот более безопасная версия ответа @ramchandran, потому что вы не знаете, сколько символов введет пользователь.

В своем ответе, если длина строки, которую ввел пользователь, меньше длины любого текста, для которого вы решите использовать ... Readmore он ... 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)
    }
}

В приведенном выше примере вывод ... Readmore составляет всего 12 символов. Если введенная пользователем строка была yourLabel.text = "12345678" то текст строки будет содержать только 8 символов. Это может ((trimmedString?.count?? 0) - readMoreLength) сбою, потому что использование диапазона ((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

Я добавил проверку безопасности, чтобы убедиться, что если введенная строка будет меньше или равна количеству символов для того, что вы решите использовать в качестве ... Readmore он вернется, и строка, которая вызовет сбой, никогда не будет достигнута:

// trimmedString is the string the user entered
guard let safeTrimmedString = trimmedString else { return }
if safeTrimmedString.count <= readMoreLength { return }

Он расположен в центре функции 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
        }
    }
}

Ответ 5

Мое решение: я создаю UIButton (name Подробнее) внизу справа и ниже UILabel. После этого я проверяю, что UILabel усечен или нет для отображения или скрытия UIButton

CGSize sizeOfText = [self.label.text boundingRectWithSize: CGSizeMake(self.label.intrinsicContentSize.width, CGFLOAT_MAX)
                                                 options: (NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading)
                                              attributes: [NSDictionary dictionaryWithObject:self.label.font forKey:NSFontAttributeName] context: nil].size;

if (self.label.intrinsicContentSize.height < ceilf(sizeOfText.height)) {
    // label is truncated
    self.readmoreButton.hidden = NO; // show Read more button
}else{
    self.readmoreButton.hidden = YES;
}

=== Swift 3 version

let textheight = self.label.text?.height(withConstrainedWidth: self.label.frame.width, font: self.label.font)
    if self.label.intrinsicContentSize.height < textheight! {
        self.readmoreButton.isHidden = false
    }else{
        self.readmoreButton.isHidden = true
    }

добавьте это расширение:

extension String {

func height(withConstrainedWidth width: CGFloat, font: UIFont) -> CGFloat {
    let constraintRect = CGSize(width: width, height: .greatestFiniteMagnitude)
    let boundingBox = self.boundingRect(with: constraintRect, options: .usesLineFragmentOrigin, attributes: [NSFontAttributeName: font], context: nil)

    return boundingBox.height
}

}

Надеемся на эту помощь

Ответ 6

Вы можете попробовать 3-ю библиотеку ExpandableLable

Задайте настраиваемый класс вашего UILabel для ExpandableLabel и установите необходимое количество строк и сжатый текст:

expandableLabel.numberOfLines = 5
expandableLabel.collapsedAttributedLink = NSAttributedString(string: "more")
expandableLabel.ellipsis = NSAttributedString(string: "...")
// update label expand or collapse state
expandableLabel.collapsed = true

Вам может потребоваться установить delegate, чтобы получить уведомление в случае, если ссылка была затронута.

Ответ 7

Свифт 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)
      }
}

Здесь я создал расширение 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
        }
      }
    }

Вам необходимо реализовать делегат didSelectLinkWithTransitInformation объекта TTTAttributedLabel. Здесь вы можете получить компонент, который вы прошли

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)
      }
    }
  }
}

Result- Перед тем, как нажать ReadMore

enter image description here

Result- После нажатия ReadMore

enter image description here

Ответ 8

Использование метода - boundingRectWithSize: options: attributes: context: и передача вашего шрифта в качестве NSFontAttributeName для NSAttributedString даст вам правильный правильный.

Из этого вам нужно проверить, превышает ли это размер вашей метки минус смещение. Только если да, вам нужно обрезать текст и показать Read More в конце.

Ответ 9

этот метод полезен для бесплодных и showAll с обновленным изображением стрелки: добавить tapgesture на этикетке

  viewcontroller.h

  @property (nonatomic,assign) BOOL isReadable; 

  viewcontrollr.m

  #pragma mark :- Tap Gesture View All
  -(void)readMoreDidClickedGesture :(UITapGestureRecognizer 
    *)objTapGesture{

     UILabel * lblDesc = (UILabel *)[objTapGesture view];
     NSLog(@"%@",lblDesc.text);
     if (self.isReadable == false) {
     [self setIsReadable:YES];
     lblDesc.text = readmoreText;
     readMoreHeight = [self getLabelHeight:lblDesc];
     }
     else{
      readMoreHeight = 30.0;
      [self setIsReadable:NO];
      }  
  }



 - (void)addReadMoreStringToUILabel:(UILabel*)label isReaded:(BOOL)isReaded
 {

  NSString *readMoreText = (isReaded == false) ? @"...Show All  " : 
   @"Show Less  ";
  NSInteger lengthForString = label.text.length;
  if (lengthForString >= 30)
  {
    NSInteger lengthForVisibleString = [self getLabelHeight:label];//[self fitString:label.text intoLabel:label];
    NSMutableString *mutableString = [[NSMutableString alloc] initWithString:label.text];
    readmoreText = mutableString;
    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 :label.font,                              NSForegroundColorAttributeName :[UIColor orangeColor]
                                                                                                                                }];
    if (isReaded == false){
        [readMoreAttributed addAttribute:NSUnderlineStyleAttributeName
                             value:@(NSUnderlineStyleSingle)
                             range:NSMakeRange(3, 8)];

        NSTextAttachment *textAttachment = [[NSTextAttachment alloc] init];
        UIImageView *imgDown = [[UIImageView alloc]initWithFrame:CGRectMake(0, 0, 25, 25)];
        imgDown.image = [UIImage imageNamed:@"searchFilterArrow1"];
        imgDown.image = [imgDown.image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
        [imgDown setTintColor:[UIColor orangeColor]];

        textAttachment.image = imgDown.image;

        NSAttributedString *attrStringWithImage = [NSAttributedString attributedStringWithAttachment:textAttachment];

        [readMoreAttributed replaceCharactersInRange:NSMakeRange(12, 1) withAttributedString:attrStringWithImage];
    }
    else{
        [readMoreAttributed addAttribute:NSUnderlineStyleAttributeName
                                   value:@(NSUnderlineStyleSingle)
                                   range:NSMakeRange(1, 9)];
        NSTextAttachment *textAttachment = [[NSTextAttachment alloc] init];
        UIImageView *imgup = [[UIImageView alloc]initWithFrame:CGRectMake(0, 0, 25, 25)];
        imgup.image = [UIImage imageNamed:@"searchFilterArrow2"];
        imgup.image = [imgup.image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
        [imgup setTintColor:[UIColor orangeColor]];

        textAttachment.image = imgup.image;

        NSAttributedString *attrStringWithImage = [NSAttributedString attributedStringWithAttachment:textAttachment];

        [readMoreAttributed replaceCharactersInRange:NSMakeRange(11, 1) withAttributedString:attrStringWithImage];
    }

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

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

    label.userInteractionEnabled = YES;
}
else {

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

}
}

Ответ 10

func updateData(_ label: UILabel) {
    self.headerLabel.text = detailViewModel.firstTitle
    self.detailLabel.text = detailViewModel.firstContent

    headerTitle = detailViewModel.firstTitle
    detailTitle = detailViewModel.firstContent

    DispatchQueue.main.async {
        let readMoreText = "...View More"
        let stringColor: UIColor = UIColor.blue
        let attributes = [NSForegroundColorAttributeName: stringColor]

        let numberOfLines = self.detailLabel.numberOfVisibleLines

        if numberOfLines > 2 {

            let lengthForVisibleString: Int = self.fit( self.detailLabel.text, into: self.detailLabel)
            let mutableString = self.detailLabel.text ?? ""
            let trimmedString = (mutableString as NSString).replacingCharacters(in: NSRange(location: lengthForVisibleString, length: (self.detailLabel?.text?.count ?? 0) - lengthForVisibleString), with: "")
            let readMoreLength: Int = readMoreText.count
            let trimmedForReadMore = (trimmedString as NSString).replacingCharacters(in: NSRange(location: trimmedString.count - readMoreLength, length: readMoreLength), with: "")
            let answerAttributed = NSMutableAttributedString(string: trimmedForReadMore, attributes: [NSFontAttributeName: self.detailLabel.font])

            let readMoreAttributed = NSMutableAttributedString(string: readMoreText, attributes: attributes)
            answerAttributed.append(readMoreAttributed)
            self.detailLabel.attributedText = answerAttributed


            let readMoreGesture = UITapGestureRecognizer(target: self, action:#selector(FundDetailsTableViewCell.showViewMore(_:)))
            readMoreGesture.numberOfTapsRequired = 1
            self.detailLabel.addGestureRecognizer(readMoreGesture)
            self.detailLabel.isUserInteractionEnabled = true
        }
    }
}

func fit(_ string: String?, into label: UILabel?) -> Int {
    guard let stringObjc = string as NSString? else {
        return 0
    }
    let font: UIFont = label?.font ?? UIFont.systemFont(ofSize: 14.0)
    let mode: NSLineBreakMode? = label?.lineBreakMode
    let labelWidth: CGFloat? = label?.frame.size.width
    let labelHeight: CGFloat? = label?.frame.size.height
    let sizeConstraint = CGSize(width: labelWidth ?? 0.0, height: CGFloat.greatestFiniteMagnitude)
    let attributes = [NSFontAttributeName: font]

    let device = UIDevice.current
    let iosVersion = Double(device.systemVersion) ?? 0

    if iosVersion > 7 {
        let attributedText = NSAttributedString(string: string ?? "", attributes: attributes)
        let boundingRect: CGRect = attributedText.boundingRect(with: sizeConstraint, options: .usesLineFragmentOrigin, context: nil)
        do {
            if boundingRect.size.height > (labelHeight ?? 0.0) {
                var index: Int = 0
                var prev: Int
                let characterSet = CharacterSet.whitespacesAndNewlines
                repeat {
                    prev = index
                    if mode == .byCharWrapping {
                        index += 1
                    } else {
                        index = Int((string as NSString?)?.rangeOfCharacter(from: characterSet, options: [], range: NSRange(location: index + 1, length: (string?.count ?? 0) - index - 1)).location ?? 0)
                    }
                } while index != NSNotFound && index < (string?.count ?? 0)
                    && (stringObjc.substring(to: index).boundingRect(with: sizeConstraint, options: .usesLineFragmentOrigin, attributes: attributes, context: nil).size.height) <= labelHeight!
                return prev;
            }
        }
    } else {
        if stringObjc.size(attributes: attributes).height > labelHeight! {
            var index: Int = 0
            var prev: Int
            let characterSet = CharacterSet.whitespacesAndNewlines
            repeat {
                prev = index
                if mode == .byCharWrapping {
                    index += 1
                } else {
                    index = stringObjc.rangeOfCharacter(from: characterSet, options: NSString.CompareOptions.caseInsensitive, range: NSRange(location: index + 1, length: stringObjc.length - index - 1)).location
                }

            } while index != NSNotFound && index < (string?.count)! && (stringObjc.substring(to: index) as NSString).size(attributes: attributes).height <= labelHeight!
            return prev

        }
    }
    return (string?.count)!
}

func showViewMore(_ sender: UITapGestureRecognizer) {

}

extension UILabel {
    var numberOfVisibleLines: Int {
        let textSize = CGSize(width: CGFloat(self.frame.size.width), height: CGFloat(MAXFLOAT))
        let rHeight: Int = lroundf(Float(self.sizeThatFits(textSize).height))
        let charSize: Int = lroundf(Float(self.font.pointSize))
        return rHeight / charSize
    }
}

Ответ 11

Для действий

 let tap = UITapGestureRecognizer(target: self, action: #selector(self.tapFunction))
 Urlabel.isUserInteractionEnabled = true
 Urlabel.addGestureRecognizer(tap)

 @objc
    func tapFunction(sender:UITapGestureRecognizer) {


    }

Ответ 12

Для Действие над меткой, если вы используете CollectionView или TableView, вы можете использовать метод делегата для выполнения действия.

func showMore(cell: CustomCell) {
    guard let indexPath = self.tableView.indexPath(for: cell) else {
        return
    }
    let cell = tableView.cellForRow(at: indexPath) as! CustomCell
    tableView.beginUpdates()
    cell.label.text = "your complete text"
    tableView.endUpdates()
}

Это обновляет метку и отображает полный текст по мере необходимости С помощью Lance Samaria ответим и добавим действие для ячейки.

Ответ 13

class DynamicLabel: UILabel{

    var fullText: String?
    var truncatedLength = 100
    var isTruncated = true

    func collapse(){
        let index = fullText!.index(fullText!.startIndex, offsetBy: truncatedLength)
        self.text = fullText![...index].description + "... More"
        isTruncated = true
    }

    func expand(){
        self.text = fullText
        isTruncated = false
    }

}

Просто простой трюк, чтобы пройти через все эти грязные реализации. Идея проста, мы не устанавливаем свернутые или расширяемые строки, просто устанавливаем метку в 0. Затем сохраните исходный текст в переменной fullText. Теперь, если мы хотим отобразить свернутый формат, просто получите подстроку и добавьте пользовательский многоточие.

Примечание: сюда не входят обработчики событий касания, вы можете добавить их самостоятельно в контроллер.

Ответ 14

Знаете ли вы, что в UILabel нет сенсорного действия. поэтому вы не можете нажать "... Подробнее", если весь текст в UILabel.

Примечание: мое решение заключается в добавлении четкой кнопки фона кнопки UILabel.