如何根据动态内容和最大尺寸限制折叠/展开视图?
Posted
技术标签:
【中文标题】如何根据动态内容和最大尺寸限制折叠/展开视图?【英文标题】:How to collapse/expand view based on dynamic content and maximum size restriction? 【发布时间】: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 中,因为它更合适。如果信息量大于屏幕会发生什么?使用您的解决方案,即使有效,用户也无法查看所有内容。
感谢您的尝试。我想知道是否有另一种方法可以开发这种设计。
【参考方案1】:
如果它可以帮助某人,我在this answer 的帮助下找到了解决方案。 将 GeneralInformationView() 包装到禁用的 ScrollView 中,并在框架修饰符中使用 minHeight 而不是 maxHeight 似乎可以解决问题!
var body: some View
VStack(spacing: 0)
ScrollView // Here adding a ScrollView
GeneralInformationView()
.background(GeometryReader geometry in
Color.clear.preference(
key: HeightPreferenceKey.self,
value: geometry.size.height
)
)
.background(Color(.white))
.frame(minHeight: calculatedHeight) // Here using minHeight instead of maxHeight
.disabled(true) // Which is disabled
if showReadMoreButton
ReadMoreButton().environmentObject(state)
// The rest is the same
【讨论】:
以上是关于如何根据动态内容和最大尺寸限制折叠/展开视图?的主要内容,如果未能解决你的问题,请参考以下文章