等高的 SwiftUI HStack 元素
SwiftUI HStack elements with equal height
我希望两个按钮具有相同的高度,类似于 UIKit 中的 Equal Height
约束。
- 不想指定frame,让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: [=10=].frame(in: .local).size.height)
})
}
.onPreferenceChange(ViewHeightKey.self) { // << read right side height
self.labelHeight = [=10=] // << 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)")
}
}
您可以在 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: [=11=].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: [=11=].frame(in: .local).size.height
)
})
}
.onPreferenceChange(ViewHeightKey.self) {
self.labelHeight = [=11=]
}
.padding(.horizontal)
}
}
注意:由于按钮现在很相似,下一步是将它们提取为另一个组件以避免重复。
我希望两个按钮具有相同的高度,类似于 UIKit 中的 Equal Height
约束。
- 不想指定frame,让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: [=10=].frame(in: .local).size.height)
})
}
.onPreferenceChange(ViewHeightKey.self) { // << read right side height
self.labelHeight = [=10=] // << 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)")
}
}
您可以在 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: [=11=].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: [=11=].frame(in: .local).size.height
)
})
}
.onPreferenceChange(ViewHeightKey.self) {
self.labelHeight = [=11=]
}
.padding(.horizontal)
}
}
注意:由于按钮现在很相似,下一步是将它们提取为另一个组件以避免重复。