在 UILabel 末尾添加“...阅读更多”
Posted
技术标签:
【中文标题】在 UILabel 末尾添加“...阅读更多”【英文标题】:Add "...Read More" to the end of UILabel 【发布时间】:2015-08-31 10:33:22 【问题描述】:我有一个UILabel
,在某些情况下,文本比UILabel
本身更长,所以我将文本视为"bla bla bla..."
我想在@987654325 末尾添加一个...Read More
按钮文本@..
我读过一些帖子,但它们提供了对我不利的解决方案,例如:计算有多少字符将输入UILabel
,但我使用的字体每个字符都有不同的宽度。
我怎样才能做到这一点?
提前致谢!
【问题讨论】:
使用 truncateinMiddle 【参考方案1】: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
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 不包含操作
【讨论】:
如何添加动作? @JesseOnolememen 您可以在标签上添加也有效的操作 我收到无法转换类型“[String : Any]?”的值到预期的参数类型“[NSAttributedStringKey:Any]?”使用 Swift 4 小心!只要字符串有一定的大小,它就可以工作,否则它会崩溃。例如,如果文本是“经理”,它不会崩溃,但如果文本是“经理”,它会崩溃。如果文本是“abcdefg”或“.......”(那里有 7 个点),它也会崩溃。每个应用程序都不同,对于我的应用程序,我让用户输入 3 到 100 个字符之间的任何文本。我使用的字体是 UIFont(name: "ArialRoundedMTRegular", size: 12)。如果您使用不同的字体,请测试字符限制,并使用简单的单音节单词进行测试,看看是否会崩溃。除此之外,这工作正常。 问题是如果文本字符的数量小于附加文本就会崩溃。例如,我没有使用“... Readmore”,而是使用了“... more”,即 8 个字符,包括空格。 “manager”和“abcdefg”这个词是 7 个字符,少于 8 个,所以它崩溃了。我将附加文本更改为“...”,它只有 3 个字符,而且效果很好。基本上标签文本中的字符数必须 >= with: 和 moreText: 参数中的字符数。【参考方案2】:这就是我将 阅读更多... 按钮添加到 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
的方法可以在here找到。
至于UITagTapGestureRecognizer
只是一个普通的UITapGestureRecognizer
子类,带有一个称为标签的NSInteger
属性。我这样做是因为我想确定点击了哪些Read More...
,以防万一我在同一个UIViewController
中有多个。你可以使用普通的UITapGestureRecognizer
。
享受吧!
【讨论】:
点击标签时如何增加标签高度 请给我这个答案的快速版本。 点击手势将作用于整个标签,而不仅仅是阅读更多文本。 我能得到这个函数的swift版本吗 Swift 版本好吗??【参考方案3】: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
【讨论】:
【参考方案4】:这适用于 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)
在上面的例子中,... 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
我添加了一个安全检查,以确保如果输入的字符串小于或等于您决定用作 ... 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
【讨论】:
感谢 Lance 让它变得更好。我正在使用此代码。它没有显示标签中的文本。我没有做任何修改,我就照原样使用它。 让 readmoreFont = UIFont(name: "Helvetica-Oblique", size: 14.0) let readmoreFontColor = UIColor.blue DispatchQueue.main.async self.movieDescLbl.addTrailing(with: self.movieDesc, moreText : "Readmore", moreTextFont: readmoreFont!, moreTextColor: readmoreFontColor) 让我们continue this discussion in chat。 self.movieDescLbl.addTrailing(with: self.movieDesc, moreText: "Readmore", moreTextFont: readmoreFont, moreTextColor: readmoreFontColor)。这里 self.movieDesc 是一个包含 40 多个字符的字符串。所以我做的是对还是错? @Raxit Pandya 我看到你的问题,你做错了,看看你写的人,这是一个简单的错误。问题在这里 addTrailing(with: self.movieDesc,它应该是 addTrailing(with: "..." 你应该在那里添加 3 个点【参考方案5】:Swift 4 和 Swift 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
您需要实现 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)
结果 - 在点击阅读更多之前
结果-点击阅读后
【讨论】:
感谢您的完整回答,除了这部分之外,它对我有用:if let _ = font self.font = font
因为我们将文本分配给标签的 `attributedText' 属性,我们需要将字体作为属性添加到字符串
我刚刚根据我的要求进行了更新,它的工作就像魅力一样。谢谢
谢谢兄弟,这正是我要找的东西
@MohyG,你是如何设法在 showTextOnTTTAttributeLable 函数中设置字体的?我已经尝试在属性中设置字体,但除了 Read More 和 Read Less 之外,整个文本都显示为黑色并且字体大小非常小。任何帮助将不胜感激!
参考这个gist.github.com/Catherine-K-George/…【参考方案6】:
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
变量中。现在如果我们想显示折叠格式,那么只需获取子字符串并添加自定义省略号。
注意:这不包括点击事件处理程序,您可以在控制器上自己添加。
【讨论】:
【参考方案7】:使用- boundingRectWithSize:options:attributes:context: 方法并将您的字体作为NSFontAttributeName
键传递给NSAttributedString
将为您提供所需的正确矩形。
您需要检查它是否大于您的标签边界减去偏移量。只有在是时,您才需要修剪文本并在末尾显示Read More
。
【讨论】:
我已经计算了原始文本的边界矩形(作为 NSAttributedString),接下来我该怎么做?我怎样才能检查有多少字符适合这个大小并主干文本并使用“...阅读更多”文本对其进行子串?【参考方案8】:你可以试试第三库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
以在链接被触摸时收到通知。
【讨论】:
仅适用于系统字体,不适用于自定义字体。 实际上我使用的是自定义字体、粗细和大小。它工作得很好,你只需要在 NSAttributedString(string:attributes:) 函数中使用它。 我对这个库有疑问,在 tableview 中“显示更多”在滚动后第一次加载时没有显示【参考方案9】:我的解决方案是,我在UILabel
的右下角和下方创建一个UIButton
(名称阅读更多)。
之后,我检查 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 版本
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
希望有帮助
【讨论】:
这仅在您希望 UILabel 下方而不是旁边的“阅读更多”选项时才有效,对吧?【参考方案10】:此方法对于 showless 和 showAll with updown 箭头图像很有用: 在标签上添加手势
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'...");
【讨论】:
【参考方案11】: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
【讨论】:
【参考方案12】:为了行动
let tap = UITapGestureRecognizer(target: self, action: #selector(self.tapFunction))
Urlabel.isUserInteractionEnabled = true
Urlabel.addGestureRecognizer(tap)
@objc
func tapFunction(sender:UITapGestureRecognizer)
【讨论】:
【参考方案13】:对于标签上的操作,如果使用 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 答案并为单元格添加操作。
【讨论】:
【参考方案14】:这是另一个使用 Swift 5 的解决方案。
参考
UITextView: Find location of ellipsis in truncated text https://***.com/users/1847511/naloiko-eugene解决方案结果
步骤
逻辑很简单。
-
检查标签的文本是否被截断。
使用 LayoutManager 获取标签文本中省略号起点的索引
使用省略号索引对文本进行切片
用
... more
替换子串(你可以改)
在标签上添加UITapGesture
从手势位置获取标签的属性字符串中的索引
-> private func getIndex(from point: CGPoint) -> Int?
检查手势位置
-> func didTapInRange(_ point: CGPoint, targetRange: NSRange) -> Bool
示例代码
let loremIpsumString = """
Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.
"""
private var expandableTextRange: NSRange?
//...in ViewDidLoad
label.text = loremIpsumString
if label.isTruncatedText
expandableTextRange = label.setExpandActionIfPossible("More", textColor: .brown)
//Add IBAction on the label
@IBAction func didTapLabel(_ sender: UITapGestureRecognizer)
guard let expandRange = expandableTextRange else
return
let tapLocation = sender.location(in: label)
if label.didTapInRange(tapLocation, targetRange: expandRange)
label.numberOfLines = 0
label.text = loremIpsumString
else
resultLabel.text = "You tapped the area outside More."
extension UILabel
var isTruncatedText: Bool
guard let height = textHeight else
return false
return height > bounds.size.height
var textHeight: CGFloat?
guard let labelText = text else
return nil
let attributes: [NSAttributedString.Key: UIFont] = [.font: font]
let labelTextSize = (labelText as NSString).boundingRect(
with: CGSize(width: frame.size.width, height: .greatestFiniteMagnitude),
options: .usesLineFragmentOrigin,
attributes: attributes,
context: nil
).size
return ceil(labelTextSize.height)
@discardableResult
func setExpandActionIfPossible(_ text: String, textColor: UIColor? = nil) -> NSRange?
guard isTruncatedText, let visibleString = visibleText else
return nil
let defaultTruncatedString = "... "
let fontAttribute: [NSAttributedString.Key: UIFont] = [.font: font]
let expandAttributedString: NSMutableAttributedString = NSMutableAttributedString(
string: defaultTruncatedString,
attributes: fontAttribute
)
let customExpandAttributes: [NSAttributedString.Key: Any] = [
.font: font as Any,
.foregroundColor: (textColor ?? self.textColor) as Any
]
let customExpandAttributedString = NSAttributedString(string: "\(text)", attributes: customExpandAttributes)
expandAttributedString.append(customExpandAttributedString)
let visibleAttributedString = NSMutableAttributedString(string: visibleString, attributes: fontAttribute)
guard visibleAttributedString.length > expandAttributedString.length else
return nil
let changeRange = NSRange(location: visibleAttributedString.length - expandAttributedString.length, length: expandAttributedString.length)
visibleAttributedString.replaceCharacters(in: changeRange, with: expandAttributedString)
attributedText = visibleAttributedString
return changeRange
var visibleText: String?
guard isTruncatedText,
let labelText = text,
let lastIndex = truncationIndex else
return nil
let visibleTextRange = NSRange(location: 0, length: lastIndex)
guard let range = Range(visibleTextRange, in: labelText) else
return nil
return String(labelText[range])
//https://***.com/questions/41628215/uitextview-find-location-of-ellipsis-in-truncated-text/63797174#63797174
var truncationIndex: Int?
guard let text = text, isTruncatedText else
return nil
let attributes: [NSAttributedString.Key: UIFont] = [.font: font]
let attributedString = NSAttributedString(string: text, attributes: attributes)
let textContainer = NSTextContainer(
size: CGSize(width: frame.size.width,
height: CGFloat.greatestFiniteMagnitude)
)
textContainer.maximumNumberOfLines = numberOfLines
textContainer.lineBreakMode = lineBreakMode
let layoutManager = NSLayoutManager()
layoutManager.addTextContainer(textContainer)
let textStorage = NSTextStorage(attributedString: attributedString)
textStorage.addLayoutManager(layoutManager)
//Determine the range of all Glpyhs within the string
var glyphRange = NSRange()
layoutManager.glyphRange(
forCharacterRange: NSMakeRange(0, attributedString.length),
actualCharacterRange: &glyphRange
)
var truncationIndex = NSNotFound
//Iterate over each 'line fragment' (each line as it's presented, according to your `textContainer.lineBreakMode`)
var i = 0
layoutManager.enumerateLineFragments(
forGlyphRange: glyphRange
) rect, usedRect, textContainer, glyphRange, stop in
if (i == self.numberOfLines - 1)
//We're now looking at the last visible line (the one at which text will be truncated)
let lineFragmentTruncatedGlyphIndex = glyphRange.location
if lineFragmentTruncatedGlyphIndex != NSNotFound
truncationIndex = layoutManager.truncatedGlyphRange(inLineFragmentForGlyphAt: lineFragmentTruncatedGlyphIndex).location
stop.pointee = true
i += 1
return truncationIndex
//https://***.com/questions/1256887/create-tap-able-links-in-the-nsattributedstring-of-a-uilabel
private func getIndex(from point: CGPoint) -> Int?
guard let attributedString = attributedText, attributedString.length > 0 else
return nil
let textStorage = NSTextStorage(attributedString: attributedString)
let layoutManager = NSLayoutManager()
textStorage.addLayoutManager(layoutManager)
let textContainer = NSTextContainer(size: frame.size)
textContainer.lineFragmentPadding = 0
textContainer.maximumNumberOfLines = numberOfLines
textContainer.lineBreakMode = lineBreakMode
layoutManager.addTextContainer(textContainer)
let index = layoutManager.characterIndex(
for: point,
in: textContainer,
fractionOfDistanceBetweenInsertionPoints: nil
)
return index
func didTapInRange(_ point: CGPoint, targetRange: NSRange) -> Bool
guard let indexOfPoint = getIndex(from: point) else
return false
return indexOfPoint > targetRange.location &&
indexOfPoint < targetRange.location + targetRange.length
【讨论】:
怎么用,没看懂【参考方案15】:TTTAttributedLabel:- 使用下面的代码行设置字体
attributedLessText = NSMutableAttributedString(string: subStringWithReadMore, 属性: [NSAttributedString.Key.font: UIFont.systemFont(ofSize: 17)])
【讨论】:
欢迎来到 Stack Overflow。在 Stack Overflow 上不鼓励仅使用代码的答案,因为它们没有解释它是如何解决问题的。请编辑您的答案,以解释它如何回答问题以及它如何改进现有答案,以便对其他有类似问题的用户有用。【参考方案16】:我无法在这里工作,所以我发布了我的解决方案。我有一个 UILabel,它应该是 3 行,在文本的末尾应该是……阅读更多。我做了一个 UIFont 扩展来计算具有特定字体和宽度的字符串的高度,然后我做了一个 String 扩展,它递归地使字符串变小,直到它适合 x 行。
extension String
/**
This method returns a substring of a string that fits in a label with a specific width and number of lines
It has optional suffix so you can add your own ellipsis, like "... Read more"
- Parameter width: The label width that constrains the text. Make sure to call it after subViews have been laid out
- Parameter lines: Number of allowed lines in the label
- Parameter font: The font to use with the label
- Parameter suffix: Custom string that will be added to the string and will fit within the width/lines constraint
- Returns: A substring that fits within the constraints given
*/
func textThatFits(width: CGFloat, lines: Int, font: UIFont, suffix: String = "") -> String
let lineHeight = font.lineHeight
let completeString = self + suffix
let size = font.sizeOfString(completeString, constrainedToWidth: width)
if size.height > lineHeight * CGFloat(lines)
let partialString = self.components(separatedBy: " ").dropLast().joined(separator: " ")
return partialString.textThatFits(width: width, lines: lines, font: font, suffix: suffix)
else
return completeString
extension UIFont
/**
Calculate the height of a string with this font and constrained width
- Parameter string: String to calculate size for
- Parameter width: The constrained width for the bounding box when calculating the size
- Returns: Size of string contained in bounding rect of width and max height
*/
func sizeOfString(_ string: String, constrainedToWidth width: Double) -> CGSize
return (string as NSString).boundingRect(
with: CGSize(width: width, height: .greatestFiniteMagnitude),
options: [.usesFontLeading, .usesLineFragmentOrigin],
attributes: [.font: self],
context: nil).size
然后我不得不从 viewDidLayoutSubviews 调用它,因为在那之前我的标签宽度不正确。最后,我是这样使用的:
private func setReadMoreText()
let readMoreSuffix = "... Read more"
let font = // YOUR_FONT
let fittingString = YOUR_LONG_STRING.textThatFits(width: YOUR_UILABEL.frame.width, lines: NUMBER_OF_LINES, font: font, suffix: readMoreSuffix)
// This is not needed but I wanted the Read more text to be colored. You could just set your fittingString directly as text.
let privacyAttrString = NSMutableAttributedString(string: fittingString, attributes: [.font: font])
privacyAttrString.addAttributes([.foregroundColor: YOUR_COLOR], range: NSRange(location: fittingString.count - readMoreSuffix.count + 4, length: readMoreSuffix.count - 4))
self.YOUR_UILABEL.text = privacyAttrString
【讨论】:
【参考方案17】:你知道UILabel没有触摸动作吗?所以如果 UILabel 中的整个文本,你不能触摸“...阅读更多”。
注意:我的解决方案是,在UILabel末尾添加一个清晰的背景按钮。
【讨论】:
以上是关于在 UILabel 末尾添加“...阅读更多”的主要内容,如果未能解决你的问题,请参考以下文章
如何在 SwiftUI 中的文本末尾实现“阅读更多”样式按钮
TTTAttributedLabel 为截断添加操作(“阅读更多”文本)
如何使用 TTTAttributedLabel 添加“阅读更多”