Ios 在Swift中的UILabel末尾显示更多/显示更少
我需要在像facebook这样的UILabel中实现show more/show less。我的标签具有提及和url功能。当文本中没有表情符号时,一切都很完美。我通过计算标签的文本高度和可见文本,在标签的末尾添加了尾随 这是我的扩展文件Ios 在Swift中的UILabel末尾显示更多/显示更少,ios,swift,uilabel,Ios,Swift,Uilabel,我需要在像facebook这样的UILabel中实现show more/show less。我的标签具有提及和url功能。当文本中没有表情符号时,一切都很完美。我通过计算标签的文本高度和可见文本,在标签的末尾添加了尾随 这是我的扩展文件 // // extension.swift // SeeMore // // Created by Macbook Pro on 14/10/20. // import Foundation import UIKit //MARK : - text h
//
// extension.swift
// SeeMore
//
// Created by Macbook Pro on 14/10/20.
//
import Foundation
import UIKit
//MARK : - text height, weidth
extension Range where Bound == String.Index {
var nsRange:NSRange {
return NSRange(location: self.lowerBound.encodedOffset,
length: self.upperBound.encodedOffset -
self.lowerBound.encodedOffset)
}
}
extension String {
var extractURLs: [URL] {
var urls : [URL] = []
var error: NSError?
let detector = try? NSDataDetector(types: NSTextCheckingResult.CheckingType.link.rawValue)
var text = self
detector!.enumerateMatches(in: text, options: [], range: NSMakeRange(0, text.count), using: { (result: NSTextCheckingResult!, flags: NSRegularExpression.MatchingFlags, stop: UnsafeMutablePointer<ObjCBool>) -> Void in
// println("\(result)")
print("Extracted Result: \(result.url)")
urls.append(result.url!)
})
return urls
}
func textHeight(withConstrainedWidth width: CGFloat, font: UIFont) -> CGFloat {
let constraintRect = CGSize(width: width, height: .greatestFiniteMagnitude)
let boundingBox = self.boundingRect(with: constraintRect, options: [.usesLineFragmentOrigin, .usesFontLeading], attributes: [NSAttributedString.Key.font: font], context: nil)
return boundingBox.height
}
func textWidth(withConstrainedHeight height: CGFloat, font: UIFont) -> CGFloat {
let constraintRect = CGSize(width: .greatestFiniteMagnitude, height: height)
let boundingBox = self.boundingRect(with: constraintRect, options: .usesLineFragmentOrigin, attributes: [NSAttributedString.Key.font: font], context: nil)
return boundingBox.width
}
}
extension UILabel{
func indexOfAttributedTextCharacterAtPoint(point: CGPoint, font : UIFont) -> Int {
guard let attributedString = self.attributedText else { return -1 }
let mutableAttribString = NSMutableAttributedString(attributedString: attributedString)
// Add font so the correct range is returned for multi-line labels
mutableAttribString.addAttributes([NSAttributedString.Key.font: font], range: NSRange(location: 0, length: attributedString.length))
let textStorage = NSTextStorage(attributedString: mutableAttribString)
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 addTrailingForShowLess(with trailingText: String, moreText: String, moreTextFont: UIFont, moreTextColor: UIColor, complition: @escaping (_ attribute: NSMutableAttributedString) -> Void) {
let readMoreText: String = trailingText + moreText
if let myText = self.text {
let trimmedString: String? = myText + trailingText
let readMoreLength: Int = (readMoreText.count)
guard let safeTrimmedString = trimmedString else { return }
if safeTrimmedString.count <= readMoreLength { return }
print("less this number \(safeTrimmedString.count) should never be less\n")
print("less 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, length: readMoreLength), with: "") + trailingText
let answerAttributed = NSMutableAttributedString(string: safeTrimmedString, attributes: [NSAttributedString.Key.font: moreTextFont])
let readMoreAttributed = NSMutableAttributedString(string: moreText, attributes: [NSAttributedString.Key.font: moreTextFont, NSAttributedString.Key.foregroundColor: moreTextColor])
answerAttributed.append(readMoreAttributed)
complition(answerAttributed)
// self.attributedText = answerAttributed
}
}
func addTrailing(with trailingText: String, moreText: String, moreTextFont: UIFont, moreTextColor: UIColor, complition: @escaping (_ attribute: NSMutableAttributedString) -> Void) {
let readMoreText: String = trailingText + moreText
if self.text?.count == 0 { return }
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 + 2)
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: moreTextFont])
let readMoreAttributed = NSMutableAttributedString(string: moreText, attributes: [NSAttributedString.Key.font: moreTextFont, NSAttributedString.Key.foregroundColor: moreTextColor])
answerAttributed.append(readMoreAttributed)
complition(answerAttributed)
// self.attributedText = answerAttributed
}
}
var visibleTextLength: Int {
let font: UIFont = self.font
let mode: NSLineBreakMode = self.lineBreakMode
let screenSize = UIScreen.main.bounds
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
}
}
}
//
//斯威夫特分机
//希莫尔
//
//由Macbook Pro于20年10月14日创建。
//
进口基金会
导入UIKit
//标记:-文字高度,宽度
扩展范围,其中Bound==String.Index{
变量nsRange:nsRange{
返回NSRange(位置:self.lowerBound.encodedOffset,
长度:self.upperBound.encodedOffset-
self.lowerBound.encodedOffset)
}
}
扩展字符串{
var-extractURL:[URL]{
变量URL:[URL]=[]
var错误:N错误?
let detector=try?NSDataDetector(类型:NSTextCheckingResult.CheckingType.link.rawValue)
var text=self
detector!.enumerateMatches(in:text,options:[],range:NSMakeRange(0,text.count),使用:{(result:NSTextCheckingResult!,标志:NSRegularExpression.MatchingFlags,stop:UnsafeMutablePointer)->Void in
//println(“\(结果)”)
打印(“提取的结果:\(Result.url)”)
url.append(result.url!)
})
返回URL
}
func textHeight(withConstrainedWidth:CGFloat,font:UIFont)->CGFloat{
let constraintRect=CGSize(宽度:宽度,高度:.greatestfinitemagnity)
让boundingBox=self.boundingRect(带:constraintRect,选项:[.usesLineFragmentOrigin,.UseSFuntleding],属性:[NSAttribute String.Key.font:font],上下文:nil)
返回boundingBox.height
}
func textWidth(带约束高度高度:CGFloat,字体:UIFont)->CGFloat{
让constraintRect=CGSize(宽度:。最大有限大小,高度:高度)
让boundingBox=self.boundingRect(带:constraintRect,选项:.usesLineFragmentOrigin,属性:[NSAttributeString.Key.font:font],上下文:nil)
返回boundingBox.width
}
}
扩展UILabel{
func indexOfAttributedTextCharacterAtPoint(point:CGPoint,font:UIFont)->Int{
guard let attributedString=self.attributedText else{return-1}
让mutableAttribString=NSMutableAttributedString(attributedString:attributedString)
//添加字体,以便为多行标签返回正确的范围
mutableAttribString.addAttributes([NSAttributedString.Key.font:font],范围:NSRange(位置:0,长度:attributedString.length))
让textStorage=NSTextStorage(AttributeString:mutableAttribString)
让layoutManager=NSLayoutManager()
textStorage.addLayoutManager(layoutManager)
让textContainer=NSTextContainer(大小:frame.size)
textContainer.lineFragmentPadding=0
textContainer.maximumNumberOfLines=numberOfLines
textContainer.lineBreakMode=lineBreakMode
layoutManager.addTextContainer(textContainer)
让index=layoutManager.characterIndex(for:point,in:textContainer,插入点之间距离的分数:nil)
回报指数
}
func addTrailingForShowLess(带有trailingText:String、moreText:String、moreTextFont:UIFont、moreTextColor:UIColor、complition:@escaping(uu属性:nsmutableAttributeString)->Void){
让readMoreText:String=trailingText+moreText
如果让myText=self.text{
让trimmedString:String?=myText+trailingText
让readMoreLength:Int=(readMoreText.count)
guard let safeTrimmedString=trimmedString else{return}
如果是safeTrimmedString.count labelHeight{
var指数:Int=0
var-prev:Int=0
让characterSet=characterSet.whitespaces和换行符
重复{
prev=指数
if mode==NSLineBreakMode.byCharWrapping{
指数+=1
}否则{
index=(myText作为NSString)。rangeOfCharacter(from:characterSet,options:[],range:NSRange(位置:index+1,长度:myText.count-index-1))。位置
}
}while index!=NSNotFound&&index
我希望它对你有用。
谢谢。我以前用过它,有时它不起作用。更重要的是,我不能添加单点击手势(这是我访问上述用户配置文件和链接url时必须使用的手势)在上面。它只允许我使用长按手势。你能提供简单的代码来演示这个问题吗?你看过吗?有人发了帖子,看起来很像你在问什么。我不想使用任何库,因为大多数时候它们与一些要求相冲突。我也不是第三方LIB的粉丝……但你可以你能用一个简单的例子来编辑你的问题吗?如果我们不必花时间去弄清楚如何设置和重现你的问题,那么提供帮助就容易多了。谢谢你的回复。我已经编辑了我的帖子,请看一下。TIA。你必须创建一个xib文件来处理它。
//
// PostTableViewCell.swift
// SeeMore
//
// Created by Macbook Pro on 14/10/20.
//
import UIKit
protocol PostTableViewCellDelegate {
func postLabelAction(cell: PostTableViewCell, post: Post, tap: UITapGestureRecognizer)
}
class PostTableViewCell: UITableViewCell {
@IBOutlet weak var postLabel: UILabel!
var currentPost : Post?
var delegate: PostTableViewCellDelegate?
override func awakeFromNib() {
super.awakeFromNib()
postLabel.textColor = .black
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(postLabelSelected))
tapGesture.numberOfTapsRequired = 1
postLabel.isUserInteractionEnabled = true
postLabel.addGestureRecognizer(tapGesture)
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
@objc func postLabelSelected(tap: UITapGestureRecognizer) {
delegate?.postLabelAction(cell: self, post: currentPost!, tap: tap)
}
func applyAttributedStringToPost(attributedString: NSMutableAttributedString, item: Post) {
let text = attributedString.string
let urls = text.extractURLs
for url in urls {
let range1 = (text as NSString).range(of: url.absoluteString)
attributedString.addAttribute(NSAttributedString.Key.foregroundColor, value: UIColor.blue, range: range1)
postLabel.attributedText = attributedString
}
postLabel.attributedText = attributedString
}
func setupItems(item : Post){
currentPost = item
let postText = item.postText
postLabel.numberOfLines = item.isExpendable ? 0 : 4
postLabel.text = postText
let underlineAttriString = NSMutableAttributedString(string: postText)
let urls = postText.extractURLs
for url in urls {
let range1 = (postText as NSString).range(of: url.absoluteString)
underlineAttriString.addAttribute(NSAttributedString.Key.foregroundColor, value: UIColor.blue, range: range1)
postLabel.attributedText = underlineAttriString
}
//Apply See more see less
postLabel.sizeToFit()
// let screenSize = UIScreen.main.bounds/
if let newfont = postLabel.font {
let textHeight = postText.textHeight(withConstrainedWidth: postLabel.frame.size.height, font: newfont)
if item.isExpendable {
postLabel.addTrailingForShowLess(with: "...", moreText: "Show Less", moreTextFont: newfont, moreTextColor: UIColor.blue) { (attributedString) in
self.applyAttributedStringToPost(attributedString: attributedString, item: item)
}
} else if postLabel.frame.size.height < textHeight, !item.isExpendable {
postLabel.addTrailing(with: "...", moreText: "Show More", moreTextFont: newfont, moreTextColor: UIColor.blue) { (attributedString) in
self.applyAttributedStringToPost(attributedString: attributedString, item: item)
}
}
}
}
}