如何根据可用空间选择SwiftUI文本视图或字符串?

如何根据可用空间选择SwiftUI文本视图或字符串?,swiftui,Swiftui,例如,如果我的UI需要以人类可读的形式显示长度测量值,它可能希望从以下格式中选择一种来显示一英寸: 1“ 1英寸 1英寸 一英寸 到目前为止,我已经尝试: 截断模式(:):只接受位置参数,不接受自定义截断选项 GeometryReader:告诉我哪些空间可用(非常有用!),但我不知道如何动态选择动态大小的子视图,似乎是为生成固定大小的子视图或溢出位置而优化的 当我试图找到另一个可能解决了这个问题的应用程序时,它们似乎都会根据方向或其他大小的变化重新排列布局。我希望继续拥有一个HStack

例如,如果我的UI需要以人类可读的形式显示长度
测量值
,它可能希望从以下格式中选择一种来显示一英寸:

  • 1“
  • 1英寸
  • 1英寸
  • 一英寸
到目前为止,我已经尝试:

  • 截断模式(:)
    :只接受位置参数,不接受自定义截断选项
  • GeometryReader
    :告诉我哪些空间可用(非常有用!),但我不知道如何动态选择动态大小的子视图,似乎是为生成固定大小的子视图或溢出位置而优化的

当我试图找到另一个可能解决了这个问题的应用程序时,它们似乎都会根据方向或其他大小的变化重新排列布局。我希望继续拥有一个
HStack
文本视图,以适应空间,尽可能避免所有重要信息被截断。

让我们定义这是一种观点:

struct FlexibleTextView: View {
    let possibleTexts: [String]

    var body: some View {
        GeometryReader { geometry in
            Text(self.possibleTexts.last(where: { $0.size(withAttributes: [.font: UIFont.systemFont(ofSize: 17.0)]).width < geometry.size.width }) ?? self.possibleTexts[0])
                .lineLimit(1)
        }
    }

    init(_ possibleTexts: [String]) {
        self.possibleTexts = possibleTexts.sorted {
            $0.size(withAttributes: [.font: UIFont.systemFont(ofSize: 17.0)]).width < $1.size(withAttributes: [.font: UIFont.systemFont(ofSize: 17.0)]).width
        }
    }
}

使用滑块,您可以调整宽度以查看效果。

对于我的用例来说不是问题,但我注意到此解决方案错误地测量了带有Unicode转义序列的字符串。因此
“1\u{2033}”
的测量长度比
“1”的测量长度长“
即使它们打印的内容相同。否则这是一个很好的解决方案,谢谢@大卫,基思,哦。。。这是个奇怪的问题。我没有这种经历;使用
打印(“1〃”.size(带有属性:[.font:UIFont.systemFont(of size:17.0)]).width;打印(“1\u{2033}.size(带属性:[.font:UIFont.systemFont(of size:17.0)])).width)
打印两次完全相同的内容。你是如何发现这个问题的?幸运的是,还有其他方法可以测量字符串的实际宽度,你可以在互联网上找到。我只是选了这个,因为它是最简单的一个…我在玩你的例子时注意到了它,我修改了排版,使用了双素数符号。我不知道我是如何进入那种状态的,因为我一直在玩,以确保我所有的边缘案例都被覆盖。由于Swift支持Unicode字符串,因此我将避免在该视图中使用转义字符串。
struct ContentView: View {
    @State var width: CGFloat = 80

    var body: some View {
        VStack {
            FlexibleTextView(["1\"", "1 in", "1 inch", "one inch"])
                .frame(width: width, height: 17)
                .border(Color.red)

            Slider(value: $width, in: 10 ... 80)
        }
        .padding()
    }
}