【问题标题】:SwiftUI HStack elements with equal heightSwiftUI HStack 元素等高
【发布时间】:2021-04-12 20:56:25
【问题描述】:

我希望两个按钮的高度相同,类似于 UIKit 中的Equal Height 约束。

  • 不想指定框架,让 SwiftUI 处理,但 HStack 中的元素应该是相同的高度。
  • 按钮应具有相同的宽度和高度,并适应较长的文本并增加其框架大小
  • 两个按钮都应显示其完整的文本(不应使用字体缩放/适合)

示例代码


struct SampleView: View {
    var body: some View {
        
        GeometryReader { gr in
            VStack {
                ScrollView {
                    VStack {
                        // Fills whatever space is left
                        Rectangle()
                            .foregroundColor(.clear)

                        Image(systemName: "applelogo")
                            .resizable()
                            .frame(width: gr.size.width * 0.5, height: gr.size.height * 0.3, alignment: .center)
                            //.border(Color.blue)
                            .padding(.bottom, gr.size.height * 0.06)


                        Text("SOME TEXT SOME TEXT SOME TEXT SOME TEXT SOME TEXT SOME TEXT SOME TEXT SOME TEXT SOME TEXT SOME TEXT SOME TEXT SOME TEXT SOME")
                            .fontWeight(.regular)
                            .foregroundColor(.green)
                            .multilineTextAlignment(.center)
                            .padding(.horizontal, 40)
                            .layoutPriority(1)


                        // Fills 15 %
                        Rectangle()
                            .frame(height: gr.size.height * 0.12)
                            .foregroundColor(.clear)

                    DynamicallyScalingView()
                        .padding(.horizontal, 20)
                        .padding(.bottom, 20)

                    }

                    // Makes the content stretch to fill the whole scroll view, but won't be limited (it can grow beyond if needed)
                    .frame(minHeight: gr.size.height)
                }
            }
        }
    }
}

struct DynamicallyScalingView: View {
    @State private var labelHeight = CGFloat.zero     // << here !!

    var body: some View {
        HStack {
            Button(action: {
            }, label: {
                Text("Button 1")
            })
            .foregroundColor(Color.white)
            .padding(.vertical)
            .frame(minWidth: 0, maxWidth: .infinity)
            .frame(minHeight: labelHeight)
            .background(Color.blue)
            .cornerRadius(8)

            Button(action: {
            }, label: {
                Text("Larger Button 2 Text Text2")
            })
            .foregroundColor(Color.white)
            .padding(.vertical)
            .frame(minWidth: 0, maxWidth: .infinity)
            .background(Color.blue)
            .cornerRadius(8)
            .background(GeometryReader {      // << set right side height
                Color.clear.preference(key: ViewHeightKey.self,
                                       value: $0.frame(in: .local).size.height)
            })
        }
        .onPreferenceChange(ViewHeightKey.self) { // << read right side height
            self.labelHeight = $0        // << here !!
        }
        .padding(.horizontal)
    }
}

struct ViewHeightKey: PreferenceKey {
    static var defaultValue: CGFloat { 0 }
    static func reduce(value: inout Value, nextValue: () -> Value) {
        value = value + nextValue()
    }
}

struct SampleView_Previews: PreviewProvider {
    static var previews: some View {
        SampleView().previewDevice("iPhone SE (2nd generation)")
    }
}

【问题讨论】:

  • 在 SwiftUI 中,父视图不能像 UIKit 中那样“强制”子视图大小。
  • 这是否回答了您的问题stackoverflow.com/a/62451599/12299030
  • 第二个按钮出现长文本怎么办,应该剪掉吗?
  • @Asperi,感谢您为我指明正确的方向。是的,它确实适用于您使用首选项键指出的方式。不幸的是,我的观点有更多的元素,让我更新问题。

标签: ios swift swiftui scrollview geometryreader


【解决方案1】:

您可以在ViewHeightKey 偏好键中设置最大值:

struct ViewHeightKey: PreferenceKey {
    static var defaultValue: CGFloat { 0 }
    static func reduce(value: inout Value, nextValue: () -> Value) {
        value = max(value, nextValue()) // set the `max` value (from both buttons)
    }
}

然后从两个按钮读取视图高度并强制垂直fixedSize

struct DynamicallyScalingView: View {
    @State private var labelHeight = CGFloat.zero

    var body: some View {
        HStack {
            Button(action: {}, label: {
                Text("SOME TEXT SOME TEXT SOME TEXT SOME TEXT SOME TEXT SOME TEXT SOME TEXT SOME TEXT SOME TEXT SOME TEXT SOME TEXT SOME TEXT SOME")
            })
                .foregroundColor(Color.white)
                .padding(.vertical)
                .frame(minWidth: 0, maxWidth: .infinity)
                .frame(minHeight: labelHeight) // min height for both buttons
                .background(Color.blue)
                .cornerRadius(8)
                .fixedSize(horizontal: false, vertical: true) // expand vertically
                .background(GeometryReader { // apply to both buttons
                    Color.clear
                        .preference(
                            key: ViewHeightKey.self,
                            value: $0.frame(in: .local).size.height
                        )
                })

            Button(action: {}, label: {
                Text("jahlsd")
            })
                .foregroundColor(Color.white)
                .padding(.vertical)
                .frame(minWidth: 0, maxWidth: .infinity)
                .frame(minHeight: labelHeight)
                .background(Color.blue)
                .cornerRadius(8)
                .fixedSize(horizontal: false, vertical: true)
                .background(GeometryReader {
                    Color.clear
                        .preference(
                            key: ViewHeightKey.self,
                            value: $0.frame(in: .local).size.height
                        )
                })
        }
        .onPreferenceChange(ViewHeightKey.self) {
            self.labelHeight = $0
        }
        .padding(.horizontal)
    }
}

注意:由于按钮现在很相似,下一步是将它们提取为另一个组件以避免重复。

【讨论】:

  • 按钮的宽度和高度应该相等。使用fixedSize() 使按钮框架剪辑为固有大小。
  • @PabloDev 你说:“按钮适应更长的文本并增加它的框架大小”“两个按钮都应该显示它们的完整文本” .如果文本长于屏幕宽度的一半,则这些约束之一是无效的。
  • 可能我不太清楚,更新了帖子描述。感谢您指出这一点。
  • @PabloDev 谢谢,但我仍然没有完全明白。如果一个按钮的文本是屏幕宽度的 2/3 怎么办?那么布局应该是什么样子呢?以及布局应该与您现在的布局有何不同(当前两个按钮相同)?
  • 按钮将增加高度以适应文本。目前,它的行为如此,但两个按钮的框架(高度)不相等。如果不清楚,请告诉我。例如:我们可以在 Xcode 中使用Environment overrides 来增加自适应字体大小,无论文本内容如何,​​两个按钮都应该具有相同的宽度和高度。如前面的要求所述,不应使用缩放字体/固定大小/填充字体。字体大小应适应设备上选择的动态字体大小
猜你喜欢
  • 1970-01-01
  • 2021-10-27
  • 2021-04-24
  • 2019-10-23
  • 2021-12-21
  • 2020-03-12
  • 1970-01-01
  • 2022-04-11
相关资源
最近更新 更多