【发布时间】:2021-12-28 12:05:23
【问题描述】:
我必须在 SwiftUI 中支持 iOS 13,并且我必须在图像上实现一个视图。
当用户点击阅读更多按钮时,视图会展开以显示所有内容。如果视图可以容纳所有内容,则没有阅读更多按钮。
如何在展开/折叠此视图时动态更改高度?
该 api 返回带有样式信息的文本数组或列表对象。当我构建一般信息视图时,我在 VStack{ ForEach { ... } } 中遍历它们。我已在此处附上简化代码以供参考。
使用下面的代码,这是我目前所拥有的,当我折叠视图(限制 maxHeight)时,我得到了:
查看外部 VStack(灰色)如何正确调整大小,但 GeneralInformationView 仍然很大。我尝试剪切它,但它只显示文本的中心。
class ViewState: ObservableObject {
@Published var isExpanded: Bool = false
@Published var fullHeight: CGFloat = 0
}
struct ContentView: View {
@ObservedObject var state: ViewState = ViewState()
let maximumHeight: CGFloat = 200
var showReadMoreButton: Bool {
if state.isExpanded {
return true
} else {
return state.fullHeight > maximumHeight
}
}
var calculatedHeight: CGFloat {
if !state.isExpanded && state.fullHeight > maximumHeight {
return maximumHeight
} else {
return state.fullHeight
}
}
var body: some View {
VStack(spacing: 0) {
GeneralInformationView()
.background(GeometryReader { geometry in
Color.clear.preference(
key: HeightPreferenceKey.self,
value: geometry.size.height
)
})
.background(Color(.white))
.frame(maxHeight: calculatedHeight)
if showReadMoreButton {
ReadMoreButton().environmentObject(state)
}
}
.padding(.all, 16)
.frame(maxWidth: .infinity, maxHeight: calculatedHeight + (showReadMoreButton ? 60 : 0) // 60 is the read more button size
.onPreferenceChange(HeightPreferenceKey.self) {
state.fullHeight = $0
}
.background(Color(.gray))
}
struct HeightPreferenceKey: PreferenceKey {
static let defaultValue: CGFloat = 0
static func reduce(value: inout CGFloat,
nextValue: () -> CGFloat) {
value = max(value, nextValue())
}
}
}
struct GeneralInformationView: View {
var body: some View {
VStack(spacing: 8) {
Text("I am a title and I could be of any length.")
.fixedSize(horizontal: false, vertical: true)
.frame(maxWidth: .infinity, alignment: .leading)
Text("""
I am a text view but actually a list with bulletpoints!
- I can be of any size
- I am received by API
- I must not be trunkated!
- If I don't fit into the outer view when collapsed,
then I should just be clipped, from the top of course
""")
.fixedSize(horizontal: false, vertical: true)
.frame(maxWidth: .infinity, alignment: .leading)
.multilineTextAlignment(.leading)
Text("I am another text here.")
.fixedSize(horizontal: false, vertical: true)
.frame(maxWidth: .infinity, alignment: .leading)
Text("I am a text and I could be of any length.")
.fixedSize(horizontal: false, vertical: true)
.frame(maxWidth: .infinity, alignment: .leading)
Text("I am a text and I could be of any length.")
.fixedSize(horizontal: false, vertical: true)
.frame(maxWidth: .infinity, alignment: .leading)
Text("I am a text and I could be of any length.")
.fixedSize(horizontal: false, vertical: true)
.frame(maxWidth: .infinity, alignment: .leading)
}
}
}
struct ReadMoreButton: View {
@EnvironmentObject var state: ViewState
var body: some View {
Button(action: {
state.isExpanded.toggle()
}, label: {
HStack {
Text(state.isExpanded ? "Collapse" : "Read More")
}
.frame(maxWidth: .infinity, minHeight: 60, maxHeight: 60, alignment: .center)
.foregroundColor(Color(.red))
.background(Color(.white))
}).overlay(Rectangle()
.foregroundColor(.clear)
.background(LinearGradient(
gradient: Gradient(colors: [.white.opacity(0),
(state.isExpanded ? .white.opacity(0) : .white.opacity(1))]),
startPoint: .top,
endPoint: .bottom))
.frame(height: 25)
.alignmentGuide(.top) { $0[.top] + 25 },
alignment: .top)
}
}
【问题讨论】:
-
这需要Minimal, Reproducible Example。您的代码中没有任何地方显示
state或GeneralInformationView是什么,因此很难解释您的意图。但是,首选项键读取视图的实际当前高度,因此您可以将其应用到其他地方。它不会计算视图可能具有不同数据的高度。 -
谢谢,我添加了一个最小的可重现示例!
-
我已经玩了几天了。我得出的结论是,鉴于您发布的 MRE,以这种方式限制它是不可能的。我能做到的最接近的方法是让窗口看起来像您想要的图片,但文字会像您的其他图片一样溢出。即使我解决了这个问题,我也会建议你把它放在 ScrollView 中,因为它更合适。如果信息量大于屏幕会发生什么?使用您的解决方案,即使有效,用户也无法查看所有内容。
-
感谢您的尝试。我想知道是否有另一种方法可以开发这种设计。