SwiftUI中如何实现单行文本的两端截断显示?
SwiftUI实现首尾显示的单行文本(中间带省略号)
默认的Text组件配合.lineLimit(1)只会在文本末尾添加省略号,要实现开头保留部分内容、结尾保留部分内容、中间用省略号填充的效果,需要自定义文本截断逻辑。以下是具体实现方案:
自定义截断文本View
通过计算文本宽度,拆分原文本为开头、省略号、结尾三部分,确保总宽度不超过设定的最大宽度:
import SwiftUI struct TruncatedText: View { let fullText: String let maxWidth: CGFloat let font: Font var body: some View { Text(truncatedResult) .font(font) } private var truncatedResult: String { let uiFont = font.toUIFont() let fullTextSize = fullText.size(withAttributes: [.font: uiFont]) // 文本未超出宽度,直接返回原内容 guard fullTextSize.width > maxWidth else { return fullText } let ellipsis = "..." let ellipsisWidth = ellipsis.size(withAttributes: [.font: uiFont]).width let availableContentWidth = maxWidth - ellipsisWidth // 计算开头可显示的字符范围 var startCursor = fullText.startIndex var startAccumulatedWidth: CGFloat = 0 let halfAvailableWidth = availableContentWidth / 2 while startAccumulatedWidth < halfAvailableWidth, startCursor < fullText.endIndex { let nextCursor = fullText.index(after: startCursor) let charWidth = fullText[startCursor..<nextCursor].size(withAttributes: [.font: uiFont]).width if startAccumulatedWidth + charWidth > halfAvailableWidth { break } startAccumulatedWidth += charWidth startCursor = nextCursor } // 计算结尾可显示的字符范围 var endCursor = fullText.endIndex var endAccumulatedWidth: CGFloat = 0 while endAccumulatedWidth < halfAvailableWidth, endCursor > fullText.startIndex { let prevCursor = fullText.index(before: endCursor) let charWidth = fullText[prevCursor..<endCursor].size(withAttributes: [.font: uiFont]).width if endAccumulatedWidth + charWidth > halfAvailableWidth { break } endAccumulatedWidth += charWidth endCursor = prevCursor } // 拼接结果 return "\(fullText[..<startCursor])\(ellipsis)\(fullText[endCursor...])" } } // 扩展Font,实现转UIFont的逻辑 extension Font { func toUIFont() -> UIFont { switch self { case .largeTitle: return UIFont.preferredFont(forTextStyle: .largeTitle) case .title: return UIFont.preferredFont(forTextStyle: .title1) case .title2: return UIFont.preferredFont(forTextStyle: .title2) case .title3: return UIFont.preferredFont(forTextStyle: .title3) case .headline: return UIFont.preferredFont(forTextStyle: .headline) case .subheadline: return UIFont.preferredFont(forTextStyle: .subheadline) case .body: return UIFont.preferredFont(forTextStyle: .body) case .callout: return UIFont.preferredFont(forTextStyle: .callout) case .footnote: return UIFont.preferredFont(forTextStyle: .footnote) case .caption: return UIFont.preferredFont(forTextStyle: .caption1) case .caption2: return UIFont.preferredFont(forTextStyle: .caption2) default: return UIFont.systemFont(ofSize: UIFont.systemFontSize) } } } // 使用示例 struct ContentView: View { let longText = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum." var body: some View { TruncatedText( fullText: longText, maxWidth: UIScreen.main.bounds.width - 40, // 留出左右padding font: .body ) .padding() } }
逻辑说明
- 先判断原文本是否超出设定的最大宽度,未超出则直接返回原内容
- 计算省略号的宽度,得到可分配给开头和结尾文本的总宽度
- 分别从文本开头和结尾遍历,计算能容纳的字符范围,确保两部分宽度之和不超过可用宽度
- 拼接开头文本、省略号、结尾文本,得到最终显示内容
内容的提问来源于stack exchange,提问作者swiftPunk
相关产品推荐
相关产品推荐

