【问题标题】:SwiftUI: Tricky layouts with pinning and neighbors?SwiftUI:带有固定和邻居的棘手布局?
【发布时间】:2020-07-11 00:25:04
【问题描述】:

使用老式自动布局很容易实现以下布局,但我不知道如何使用 SwiftUI 做同样的事情。这是我要渲染的视图示例:

这显示了同一视图的 3 个变体。该视图有 3 个文本子项。左、中、右,具有以下属性:

  • 左右视图固定在父视图的前导和尾随,中心视图始终保持在父视图的中心 (1)
  • 如果任一侧视图缺失或为空 (2),则中心视图保持居中状态
  • 中心视图会增长,直到碰到任一侧视图,然后剪切其内容 (3)

我能做的最好的就是下面的这个示例,但它的缺点是中心视图优先于侧视图,因此会剪辑它们。

如何使用 SwiftUI 布局实现这一点?

import SwiftUI

struct MyExample: View {

    let leftText:String
    let mainText:String
    let rightText:String

    var body: some View {
        HStack{
            HStack {
                Text(self.leftText)
                    .modifier(MyBigFont())

                Spacer()
            }

            Text(self.mainText)
                .modifier(MyBigFont())
                .layoutPriority(1.0)

            HStack {
                Spacer()
                Text(self.rightText)
                    .modifier(MyBigFont())

            }
        }
    }

}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        VStack {
            MyExample(leftText:"ABC", mainText:"1234", rightText:"XYZ").padding()
            MyExample(leftText:"", mainText:"1234", rightText:"XYZ").padding()
            MyExample(leftText:"ABC", mainText:"1234567890", rightText:"XYZ").padding()
            Spacer()
        }
    }
}

struct MyBigFont: ViewModifier {
    func body(content: Content) -> some View {
        content
            .lineLimit(1)
            .font(Font.system(size: 42).bold())
    }
}

输出:

【问题讨论】:

  • 好的,这里有几件事。首先,堆栈视图,它们是救命稻草。其次,您可以在属性面板中设置这些项目的优先级。 Apples HIG 指南建议不要截断字符串,而是建议您缩放它们。我会在可能很长的字符串上设置一个自动缩放因子,然后对需要首先设置的字符串设置优先级。
  • @xTwisteDx 是的,我同意堆栈。同样是的,自动缩放优于截断。我只是想简化这个例子。您可以在我设置优先级的示例中看到。自动缩放对我发布的示例没有帮助,因为它只会在 截断侧视图并用完空间后开始缩放。

标签: ios swift swiftui


【解决方案1】:

尝试根据需要使用您的 ZStack,它会节省您的代码行数。例如,这里是如何简单地显示一个标题,其中包含一个左对齐的项目,一个项目在同一行中居中对齐:

ZStack(){
HStack{
    Image("star").resizable().foregroundColor(.white).frame(width: 50, height: 50)
    Spacer()
}
    Image("star").resizable().font(.title).foregroundColor(.white).frame(width: 50, height: 50) }

【讨论】:

  • 这绝对不符合我列出的第三个要求:“中心视图会增长,直到它碰到任一侧视图,然后剪辑其内容”。我需要所有 3 个视图才能水平交互。 Z 堆栈无法做到这一点。
【解决方案2】:

如果左边或右边太宽以至于需要截断,这可以使中间部分不居中,但除此之外,我相信在文本旁边添加fixedSize() 就足够了。文本上的fixedSize() 表示不允许截断或换行,这正是您要查找的内容。

var body: some View {
    HStack{
        HStack {
            Text(self.leftText)
                .modifier(MyBigFont())
                .fixedSize()   // <====

            Spacer()
        }

        Text(self.mainText)
            .modifier(MyBigFont())
            .padding(.horizontal)
            .layoutPriority(1.0)


        HStack {
            Spacer()
            Text(self.rightText)
                .modifier(MyBigFont())
                .fixedSize()    // <====
        }
    }
}


您在评论中的问题非常好,并且是解释其工作原理的绝佳借口。这里没有什么是棘手的 IMO,所以很好理解它。

来自 'layoutPriority' 文档:“父布局应为具有最高布局优先级的子级提供所有提供给父级的空间减去其所有低优先级子级所需的最小空间,等等每个较低优先级值。” (强调我的)这是否意味着 SwiftUI 应该为侧文本提供最小大小以适应其内容?

SwiftUI is 为侧文本提供最小尺寸以适应其内容。带有尾部截断的文本的 最小 大小是绘制一个字符和一个省略号所需的空间。 理想(水平)大小是在一行上绘制所有文本而不换行所需的大小。

所以外部 HStack 首先询问两个内部 HStack 的最小尺寸。他们反过来询问他们的 Text 的最小大小(第一个字符加上省略号),并且 Spacer 的最小值为零。

然后外部 HStack 取这两个最小值并将它们从其总数中减去,并将其提供给中心 Text。

当您添加 fixedSize 时,它会告诉 Text 报告其最小大小作为在一行上绘制整个字符串所需的大小,因此现在每个内部 HStack 都将其作为最小值,并且中心 Text 提供的内容更少空间。

在这两种情况下,中心的 Text 然后告诉外部 HStack 它将使用多少空间,必要时截断(因为它没有固定大小)。

外部 HStack 减去该空间,然后考虑其他两个孩子。它们同样灵活(因为两者都包含一个 Spacer),所以它提供了左侧一半的空间。左边的那个完全拿走它提供的东西 (*),所以另一半提供给右边的那个,右边的也拿走所有的。

(*) 在大多数情况下,我们在这里讨论。在足够窄的容器中,所需的最小尺寸可能会大于可用的尺寸。在这种情况下,孩子会得到比提供的更多的东西,事情就会超出他们的界限。

【讨论】:

  • 这很好用 Rob!不过,这对我来说并不直观,为什么需要这样做。来自“layoutPriority”文档:“父布局应为具有最高布局优先级的子级提供所有提供给父级的空间减去其所有低优先级子级所需的最小空间,依此类推每个较低的优先级值。” (强调我的)这是否意味着 SwiftUI 应该为侧文本提供最小大小以适合其内容?知道为什么在没有 fixedSize() 修饰符的情况下它们会折叠吗?
  • 感谢罗布的详细说明。这很有意义。 (我的直觉是“最小空间”==“内在大小”来自自动布局。)
  • 这不满足要求(3),以防万一,但是......这很简单,绝对。 @MikeBernardo。
  • @asperi 是的,确实如此。中心视图只会增长,直到它碰到标有 .fixedSize() 的侧面文本
猜你喜欢
  • 2011-01-24
  • 2016-02-03
  • 2013-11-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多