美文网首页
换行文字指定位置截断

换行文字指定位置截断

作者: 风吹柳絮如花落 | 来源:发表于2021-07-04 13:54 被阅读0次

案例提要

字符串分为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秒)

相关文章

网友评论

      本文标题:换行文字指定位置截断

      本文链接:https://www.haomeiwen.com/subject/aoimultx.html