SwiftUI中如何实现双文本自适应同排/换行布局?
SwiftUI实现自适应文本布局方案
针对你提出的长文本与短文本的自适应布局需求(最后一行空间足够时短文本嵌入末尾,不足时上下排列),可以借助iOS 17+引入的TextRenderer API结合自定义Layout协议实现,具体方案如下:
核心思路
- 用
TextRenderer解析长文本的布局细节,获取每一行的宽度和位置信息。 - 计算容器宽度下长文本最后一行的剩余空间,对比短文本的实际宽度。
- 通过自定义
Layout动态调整布局:空间足够时将短文本放置在长文本最后一行末尾;不足时采用上下排列的布局。
完整代码实现
import SwiftUI // 自定义布局,处理自适应文本排列 struct AdaptiveTextLayout: Layout { let longText: String let shortText: String let shortTextFont: Font func sizeThatFits(proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) -> CGSize { let containerWidth = proposal.width ?? UIScreen.main.bounds.width let longTextRenderer = TextRenderer(Text(longText)) let longLayout = longTextRenderer.layout(size: .init(width: containerWidth, height: .infinity)) let shortTextSize = subviews[1].sizeThatFits(proposal) guard let lastLine = longLayout.lines.last else { return CGSize(width: containerWidth, height: shortTextSize.height) } let remainingWidth = containerWidth - lastLine.size.width if remainingWidth >= shortTextSize.width { return CGSize(width: containerWidth, height: longLayout.totalSize.height) } else { let longTextHeight = subviews[0].sizeThatFits(proposal).height return CGSize(width: containerWidth, height: longTextHeight + shortTextSize.height + 8) // 8为上下文本间距 } } func placeSubviews(in bounds: CGRect, proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) { let containerWidth = bounds.width let longTextRenderer = TextRenderer(Text(longText)) let longLayout = longTextRenderer.layout(size: .init(width: containerWidth, height: .infinity)) let shortTextSize = subviews[1].sizeThatFits(proposal) guard let lastLine = longLayout.lines.last else { // 长文本为空时,短文本右对齐放置 subviews[1].place(at: CGPoint(x: bounds.maxX - shortTextSize.width, y: bounds.minY), proposal: proposal) return } let remainingWidth = containerWidth - lastLine.size.width if remainingWidth >= shortTextSize.width { // 放置长文本 subviews[0].place(at: bounds.origin, proposal: proposal) // 计算短文本位置:最后一行末尾垂直居中 let shortTextX = bounds.minX + lastLine.size.width + (remainingWidth - shortTextSize.width) let totalLineHeight = longLayout.lines.dropLast().reduce(0) { $0 + $1.size.height } let shortTextY = bounds.minY + totalLineHeight + (lastLine.size.height - shortTextSize.height)/2 subviews[1].place(at: CGPoint(x: shortTextX, y: shortTextY), proposal: proposal) } else { // 上下排列:长文本左对齐,短文本右对齐 subviews[0].place(at: bounds.origin, proposal: proposal) let longTextHeight = subviews[0].sizeThatFits(proposal).height subviews[1].place(at: CGPoint(x: bounds.maxX - shortTextSize.width, y: bounds.minY + longTextHeight + 8), proposal: proposal) } } } // 封装成可复用的View struct AdaptiveTextPair: View { let longText: String let shortText: String let shortTextFont: Font = .caption var body: some View { AdaptiveTextLayout(longText: longText, shortText: shortText, shortTextFont: shortTextFont) { Text(longText) Text(shortText) .font(shortTextFont) } } } // 使用示例 struct TextTest: View { var body: some View { AdaptiveTextPair( 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, sa laborum.", shortText: "(c) 2024" ) .padding() Spacer() } }
说明
- 该方案依赖iOS 17及以上系统,因为
TextRenderer是iOS 17新增的API,用于解析文本的底层布局信息。 - 自定义
Layout协议负责完成布局尺寸计算和子视图位置摆放,确保两种场景的自动切换。 - 之前尝试的文本拼接无法单独控制短文本的对齐和字体;SwiftUI的
AttributedString暂不支持段落级对齐控制,因此需要通过TextRenderer手动计算布局来实现需求。
内容的提问来源于stack exchange,提问作者hanno
相关产品推荐
相关产品推荐

