J'ai un UILabel
et dans certains cas, le texte est plus long que le UILabel
lui-même, je le vois donc sous la forme "bla bla bla..."
Je souhaite ajouter un texte de bouton ...Read More
à la fin de la UILabel
..
J'ai lu quelques articles mais ils proposent des solutions qui ne me conviennent pas, par exemple: calculer combien de caractères entreront la UILabel
, mais avec la police que j'utilise, chaque caractère a une largeur différente.
Comment puis-je réussir à faire ça?
Merci d'avance!
C'est donc ce que j'ai fait pour ajouter le bouton Read More ... à la variable UITextView
, UITextField
ou 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'...");
}
}
Il existe une utilisation de la méthode fitString:intoLabel
qui peut être trouvée ici .
En ce qui concerne UITagTapGestureRecognizer
, il n’ya qu’une sous-classe UITapGestureRecognizer
normale avec une propriété NSInteger
appelée tag. Je l'ai fait parce que je voulais identifier le Read More...
sur lequel j'ai cliqué dans le cas où j'en aurais plus d'un dans la même UIViewController
. Vous pouvez utiliser une UITapGestureRecognizer
normale.
Prendre plaisir!
Swift4 (IOS 11.2)
Readmore à la fin de l'étiquette sans action
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
}
}
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
}
}
Usage
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)
}
Résultat
NOTE: - L'action n'est pas incluse pour Readmore
L'étiquette Tttattributed a cette fonctionnalité
https://github.com/TTTAttributedLabel/TTTAttributedLabel
Vous devez définir le jeton "Troncature" comme "En savoir plus ..."
Voir
attribuéTruncationToken
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
Ma solution est la suivante: je crée une UIButton
(nom, en savoir plus) en bas à droite et au-dessous de la UILabel
. Après cela, je vérifie que la UILabel
est tronquée ou non pour afficher ou masquer la 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;
}
=== version 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
}
ajoutez cette extension:
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
}
}
J'espère que cette aide
Vous pouvez essayer la 3ème bibliothèque ExpandableLable
Définissez la classe personnalisée de votre UILabel sur ExpandableLabel et définissez le nombre souhaité de lignes et de texte réduit:
expandableLabel.numberOfLines = 5
expandableLabel.collapsedAttributedLink = NSAttributedString(string: "more")
expandableLabel.Ellipsis = NSAttributedString(string: "...")
// update label expand or collapse state
expandableLabel.collapsed = true
Vous devrez peut-être définir une delegate
pour être averti au cas où le lien aurait été touché.
En utilisant la méthode - boundingRectWithSize: options: attributs: contexte: et en passant votre police sous la forme de la clé NSFontAttributeName
pour NSAttributedString
, vous obtiendrez le bon rect approprié.
À partir de là, vous devez vérifier si elle est plus grande que les limites de votre étiquette moins le décalage. Si seulement oui, vous devez couper votre texte et afficher Read More
à la fin.
cette méthode est utile pour showless et showAll avec une image de flèche de mise à jour: add tapgesture on label
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'...");
}
}
Savez-vous qu'il n'y a pas d'action tactile d'UILabel. vous ne pouvez donc pas toucher '... Lire la suite' si le texte entier d'un UILabel.
Remarque: ma solution est d’ajouter un bouton d’arrière-plan transparent à la fin de UILabel.
Pour l'action
let tap = UITapGestureRecognizer(target: self, action: #selector(self.tapFunction))
Urlabel.isUserInteractionEnabled = true
Urlabel.addGestureRecognizer(tap)
@objc
func tapFunction(sender:UITapGestureRecognizer) {
}
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
}
}