Agregue “… Leer más” al final de UILabel

Tengo un UILabel y en algunos casos el texto es más largo que el propio UILabel , por lo que veo el texto como "bla bla bla..." Quiero agregar un texto del botón ...Read More al final de UILabel . .

He leído algunas publicaciones, pero ofrecen soluciones que no son buenas para mí, por ejemplo: para calcular cuántos caracteres ingresarán en UILabel , pero con la fuente que estoy usando, cada personaje tiene un ancho diferente.

¿Cómo puedo hacer eso?

¡Gracias por adelantado!

Swift4 (IOS 11.2)

Lea más al final de la etiqueta sin acción

 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: [NSAttributedStringKey.font: self.font]) let readMoreAttributed = NSMutableAttributedString(string: moreText, attributes: [NSAttributedStringKey.font: moreTextFont, NSAttributedStringKey.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] = [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 } } 

Uso

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

Resultado

Salida de etiqueta Readmore

NOTA: - La acción no está incluida para Readmore

Así que esto es lo que hice para agregar el botón Leer Más … al UITextView , UITextField o 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'..."); } } 

Hay un uso del método fitString:intoLabel que se puede encontrar aquí .

En cuanto a UITagTapGestureRecognizer es solo una subclase UITapGestureRecognizer normal con una propiedad NSInteger llamada tag. Lo hice porque quiero identificar en qué Read More... hice clic en caso de que tenga más de uno en el mismo UIViewController . Puede usar un UITapGestureRecognizer normal.

¡Disfrutar!

La etiqueta atribuida tiene esta característica

https://github.com/TTTAttributedLabel/TTTAttributedLabel

Debes configurar el token de “truncamiento” como “leer más …”

Ver

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 

Mi solución es, creo un UIButton (nombre Leer más) en la parte inferior derecha y debajo de UILabel . Después de eso, compruebo que el UILabel está truncado o no para mostrar u ocultar el 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; } 

=== Versión de Swift 3

 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 } 

agregue esta extensión:

 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 } 

}

Espero que esto ayude

Puedes probar la tercera biblioteca ExpandableLable

Establezca la clase personalizada de su UILabel en ExpandableLabel y establezca el número deseado de líneas y texto contraído:

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

Es posible que necesite configurar un delegate para recibir notificaciones en caso de que se haya tocado el enlace.

Usando el método – boundingRectWithSize: options: attributes: context: y pasar su fuente como la clave NSAttributedString para NSAttributedString le dará la corrección correcta necesaria.

Para eso, debe verificar si es más grande que los límites de su etiqueta menos el desplazamiento. Solo en caso afirmativo, debe recortar el texto y mostrar Read More al final.

¿Sabes que no hay acción táctil de UILabel? así que no puedes tocar ‘… Leer más’ si todo el texto está en UILabel.

Nota: mi solución es agregar un botón de fondo claro al final de UILabel.

este método es útil para showless y showAll con imagen de flecha hacia arriba: agregar tapgesture en la etiqueta

  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'..."); } } 
 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) { } 

extensión 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}}