SwiftUI Divider 无法调整自身大小以适应父视图?

SwiftUI Divider not sizing itself to fit parent view?

提问人:Jacob King 提问时间:11/10/2023 最后编辑:Dávid PásztorJacob King 更新时间:11/11/2023 访问量:74

问:

我有一个相当基本的 SwiftUI 视图层次结构,我正在尝试在其中插入一个分隔符,但是分隔符正在将自己调整为某个模糊的值,并将父视图推出以满足它。层次结构中的所有其他视图都运行正常,并且父视图正在包装最大的子视图,正如预期的那样。

我已经搜索了又搜索,只是找不到对这种行为的任何见解 - 所以我问你们一些好人。

查看代码:

Form {
    HStack(alignment: .top) {
        VStack(alignment: .trailing) {
            Picker("Paper Size:", selection: $viewModel.layoutParameters.paperSize) {
                ForEach(PrintSize.allCases, id: \.self) {
                    Text($0.description)
                }
            }
            .pickerStyle(.menu)
                    
            Picker(selection: $viewModel.layoutParameters.margin) {
                ForEach(Margin.allCases, id: \.self) {
                    Text($0.description)
                }
            } label: {
                Text("Margins:")
                    .padding(.leading, 15) // This is janky, find a better way to do it.
            }
            .pickerStyle(.menu)
        }
                
        Divider()
                
        Picker("Default Print Size:", selection: $viewModel.layoutParameters.printSize) {
            ForEach(PrintSize.allCases, id: \.self) {
                Text($0.description)
            }
        }
        .pickerStyle(.menu)
    }
}
.padding(8)

使用分隔器时表现出的行为:
With divider

不带分隔线:
enter image description here

如果您能给我一种方法来在不显式应用填充的情况下将 Margins 标签推过来,那就加分了。

Swift macOS SwiftUI 分隔器

评论

0赞 Mojtaba Hosseini 11/10/2023
我不明白这个问题。Mac 窗口可调整大小,系统会存储最后的设置。你希望它是固定大小的吗?
0赞 Mojtaba Hosseini 11/10/2023
您可以使用摆脱手动填充(但您应该指定正确的公式).fixedSize()

答:

1赞 Benzy Neez 11/10/2023 #1

您描述的两个问题都可以通过使用叠加来解决。

  1. A 是贪婪的,并扩展以使用所有可用空间,就像 .要将其限制在 的高度,只需将其放在 上面的覆盖层中即可。默认情况下,叠加层将与 .这是完美的,因为两者的宽度相等,因此将自动定位在它们之间的中间。但是,需要两个额外的步骤才能使其看起来正确:DividerSpacerHStackHStackHStackPickerDivider
  • a 的方向通常由包含它的堆栈决定,或由 detault 水平确定。由于您希望它是垂直的,因此需要将其嵌套在虚拟 .DividerHStack
  • 您可能希望增加 ,因为现在两者之间只有一个空格。spacingHStackPicker
  1. 如果希望“边距”标签的宽度与“纸张尺寸”标签的宽度相同,则可以使用较长标签的隐藏版本来建立所需的占用空间。然后在叠加层中显示较短的标签。

下面是示例的更新版本,其中应用了两项更改:

Form {
    HStack(alignment: .top, spacing: 20) { // <- spacing added
        VStack(alignment: .trailing) {
            Picker("Paper Size:", selection: $viewModel.layoutParameters.paperSize) {
                ForEach(PrintSize.allCases, id: \.self) {
                    Text($0.description)
                }
            }
            .pickerStyle(.menu)

            Picker(selection: $viewModel.layoutParameters.margin) {
                ForEach(Margin.allCases, id: \.self) {
                    Text($0.description)
                }
            } label: {
                Text("Paper Size:")
                    .hidden()
                    .overlay(alignment: .trailing) {
                        Text("Margins:")
                    }
//                    .padding(.leading, 15) // This is janky, find a better way to do it.
            }
            .pickerStyle(.menu)
        }

//        Divider()

        Picker("Default Print Size:", selection: $viewModel.layoutParameters.printSize) {
            ForEach(PrintSize.allCases, id: \.self) {
                Text($0.description)
            }
        }
        .pickerStyle(.menu)
    }
    .overlay {
        HStack {
            Divider()
        }
    }
}
.padding(8)

Screenshot

评论

1赞 Mojtaba Hosseini 11/10/2023
您可以添加 a 作为占位符,而不是硬编码的间距hiddenfixedSizedDivider
0赞 Mojtaba Hosseini 11/10/2023
您也可以在主视图上分解并应用它一次。.pickerStyle(.menu)
0赞 Benzy Neez 11/10/2023
@MojtabaHosseini 固定大小的隐藏隔板确实有效,感谢您的建议。
0赞 Jacob King 11/20/2023
我不知道叠加层是一回事,所以这是一个有用的评估 - 谢谢。经验证已解决问题,尽管我仍然不确定我是否更喜欢虚拟标签(对我来说有点代码味道)还是看起来更“合适”的网格解决方案,尽管设计过度。时间会证明哪些是留下来的,但两者都被验证为有效。
1赞 Sweeper 11/10/2023 #2

请注意,您使用的是 Xcode 预览。预览的行为与正常运行应用的行为略有不同。

如果您正常运行应用程序,则两种情况下的窗口大小将相同(无论 macOS 默认窗口大小是多少,或者存储在您的首选项中的任何内容),并且您可以将两者的大小调整为尽可能小的大小。您看到的尺寸只是 Xcode 预览的怪癖。

请注意,如果没有 ,您仍然可以将窗口的大小调整为任意大,并且位于中间,而无需更改其高度,并且下方和上方都有很多空白区域。另一方面,s 会水平扩展,s 的宽度可以改变。DividerHStackPickerHStack

“预览”尝试显示您的视图,而不显示任何周围的空白区域。这就是为什么你会得到一个非常适合的窗口,当没有.请注意,预览窗口的宽度是默认的 macOS 窗口宽度。这是因为选择者很贪婪,并试图占用尽可能多的水平空间 - 预览必须在某个地方停止它。HStackDivider

Dividers也很贪婪,并试图尽可能地垂直扩张。当存在 时,当 的高度大于拾取器的总和时,所有 都会在顶部对齐(因为)。此外,上下不再有“周围的空白区域”,因为总有填充该空间。预览会选择 macOS 窗口的默认高度(同样,它必须在某处停止扩展),该数字大于选取器的总高度。DividerTextHStack(alignment: .top)HStackDivider

预览本可以设计为将窗口大小调整为最小大小,但事实并非如此。我想很多观点这样看起来会相当局促。

我不确定有没有办法让预览显示最小大小窗口。不过,您可以强制预览使用特定大小的窗口:

#Preview(traits: .fixedLayout(width: 500, height: 100)) {
    ContentView()
}

您也可以尝试穿上 ,但这会调整为“理想尺寸”,而不是最小尺寸。“理想尺寸”仍然比最小尺寸大得多。.fixedSizeHStack

// you can also set the ideal size
// .frame(idealHeight: 100)
.fixedSize(horizontal: false, vertical: true)

至于对齐标签,我会使用:Grid

Grid(alignment: .trailing, horizontalSpacing: 8, verticalSpacing: 8) {
    GridRow {
        Text("Paper Size:")
        Picker("", selection: .constant(1)) {
            // Dummy data for the picker...
            Text("Foo").tag(1)
        }
    }
        
    GridRow {
        Text("Margins:")
        Picker("", selection: .constant(1)) {
            Text("Bar").tag(1)
        }
    }
}

旁注:您可以放在 上,而不是每个选择器。它将这种风格应用于所有孩子。.pickerStyle(.menu)HStack

评论

0赞 Jacob King 11/20/2023
感谢您对预览的解释,这是有道理的。此外,还有关于属性传播到儿童的提示(我觉得我知道这一点,但由于某种原因没有应用它)。网格方法感觉很过分,但比起虚拟标签,它的气味更少,所以陪审团对我保留的东西没有意见,但我已经验证了两者都有效。
-1赞 Leonardo Perez 11/11/2023 #3

这个问题令人担忧。 您在使用 SwiftUI 视图层次结构时遇到的行为可能是由于 SwiftUI 计算视图布局的方式所致。默认情况下,视图将展开以填充可用宽度。如果将其放置在具有灵活宽度的容器视图中,这可能会导致意外的布局问题,例如 . 你可以试试这个:FormDividerDividerHStack

Form {
    // Your existing code...
    
    HStack(alignment: .top) {
        VStack(alignment: .trailing) {
            // Your existing code...
        }
        
        Divider()
            .frame(height: 1) // Set the height of the Divider to 1 point
            .padding(.vertical, 8) // Add vertical padding to match the other views
            .background(Color.gray) // Optionally, add a background color to the Divider
            .padding(.horizontal, 8) // Add horizontal padding to match the other views
    }
    
    // Your existing code...
}

通过使用 frame 修改器将 的高度设置为 1 磅,您可以独立于 .此外,还可以添加填充和背景色,以匹配视图层次结构中其他视图的样式。DividerHStack

尝试此修改,看看它是否解决了 SwiftUI 视图层次结构中的布局问题。Divider