案例提要
字符串分为pre + [abc] + next三段拼接,其中abc为长度变化的字符串,故截断位置为abc的末尾方式截断
UI效果
文案为
已投稿[哈哈哈哈哈-哈哈哈哈哈-哈哈哈哈哈-哈哈哈哈哈-哈哈哈哈哈-哈哈哈哈哈]等100个问题
UI为
image.png
方案
1、暴力法计算(本案例方案)
2、CoreText方式(有时间会搞一搞)
相关代码 - OC
//
// NSString+TruncateToWidth.m
// demo
//
// Created by pengshengsong on 2021/7/2.
//
#import "NSString+TruncateToWidth.h"
#define ellipsis @"…"
@implementation NSString (TruncateToWidth)
- (NSString *)stringByTruncatingAtString:(NSString *)string width:(CGFloat)width numberOfLines:(NSInteger) numberOfLines font:(UIFont *)font {
if ([self sizeWithAttributes:@{NSFontAttributeName:font}].width < width || [self rangeOfString:string].location == NSNotFound) {
return self;
}
NSDictionary *attr = @{NSFontAttributeName:font};
NSMutableString *truncatedString = [self mutableCopy];
// origin
CGFloat originHeight = [truncatedString boundingRectWithSize:CGSizeMake(width, CGFLOAT_MAX) options:NSStringDrawingUsesLineFragmentOrigin attributes:attr context:nil].size.height;
// standard
NSMutableString *standardString = [NSMutableString string];
for (NSInteger i = 1; i < numberOfLines; i ++) {
[standardString appendString:@"\n"];
}
CGFloat standardHeight = [standardString boundingRectWithSize:CGSizeMake(width, CGFLOAT_MAX) options:NSStringDrawingUsesLineFragmentOrigin attributes:attr context:nil].size.height;
if (originHeight <= standardHeight) {
return self;
}
NSRange range = [truncatedString rangeOfString:string];
range.length = 1;
//先处理...
range.location -= 1;
[truncatedString replaceCharactersInRange:range withString:ellipsis];
while (range.location > 0) {
CGFloat tempHeight = [truncatedString boundingRectWithSize:CGSizeMake(width, CGFLOAT_MAX) options:NSStringDrawingUsesLineFragmentOrigin attributes:attr context:nil].size.height;
if (tempHeight == standardHeight) {
break;
} else {
range.location -= 1;
[truncatedString deleteCharactersInRange:range];
}
}
return truncatedString;
}
@end
相关代码 - Swift
import UIKit
// Converted to Swift 5.4 by Swiftify v5.4.29596 - https://swiftify.com/
let ellipsis = "…"
extension String {
/// 处理换行截断字符串
/// - Parameters:
/// - string: ]
/// - width: label的宽度
/// - numberOfLines: label的numberOfLines
/// - font: label的font
/// - Returns: 处理后的字符串
mutating func stringByTruncating(at string: String, width: CGFloat, numberOfLines: Int, font: UIFont) -> String {
let attr: [NSAttributedString.Key : UIFont] = [NSAttributedString.Key.font: font]
// origin
let originHeight = self.boundingRect(with: CGSize(width: width, height: CGFloat.greatestFiniteMagnitude), options: .usesLineFragmentOrigin, attributes: attr, context: nil).size.height
// standard
var standardString = ""
for _ in 1..<numberOfLines {
standardString += "\n"
}
let standardHeight = standardString.boundingRect(with: CGSize(width: width, height: CGFloat.greatestFiniteMagnitude), options: .usesLineFragmentOrigin, attributes: attr, context: nil).size.height
//compare
if originHeight <= standardHeight || (self as NSString).range(of: (string ) as String).location == NSNotFound {
return self
}
//获取]的唯一位置
var range = (self as NSString).range(of: string)
range.length = 1
//先处理...
range.location -= 1
if let subRange = Range<String.Index>(range, in: self) { self.replaceSubrange(subRange, with: ellipsis) }
//处理其他
while range.location > 0 {
let tempHeight = self.boundingRect(with: CGSize(width: width, height: CGFloat.greatestFiniteMagnitude), options: .usesLineFragmentOrigin, attributes: attr, context: nil).size.height
if tempHeight == standardHeight {
break
} else {
range.location -= 1
if let subRange = Range<String.Index>(range, in: self) { self.removeSubrange(subRange) }
}
}
return self
}
}
使用
self.label.text = [self.label.text stringByTruncatingAtString:@"]" width:self.label.frame.size.width numberOfLines:self.label.numberOfLines font:self.label.font];
代码链接
https://github.com/pengshengsongcode/TruncationString
相关链接
暴力法相关
https://stackoverflow.com/questions/24783183/truncate-uilabel-in-specific-location
https://stackoverflow.com/questions/24032206/how-to-achieve-something-like-nslinebreakbytruncatinghead-for-uitextfield/24038763
CoreText相关
http://blog.sina.com.cn/s/blog_60f977e70101i9yf.html
https://github.com/Axk520/OrgHalfLineLabel
遇到的坑
1、/n是两行、/n/n是三行(某知名游戏公司高级研发工程师-旭旭看出来了这个bug,困扰了我14小时21分26秒)








网友评论