SwiftUI 将部分列表固定到底部
Posted
技术标签:
【中文标题】SwiftUI 将部分列表固定到底部【英文标题】:SwiftUI pin partial list to bottom 【发布时间】:2021-05-19 01:41:38 【问题描述】:我有一个聊天消息列表,随着新消息添加到最后,它会向上滚动。但如果视图中的消息太少,它们会固定在顶部。
有没有办法把内容固定在底部?
请注意,在这种情况下,视图位于 UIHostingController
内。
struct OverlayChatView: View
@ObservedObject public var stream: ChatStream
var body: some View
ScrollViewReader scrollView in
ScrollView
LazyVStack(alignment: .leading, spacing: 0.0)
ForEach(self.stream.messages) inMsg in
ChatMessageCell(message: inMsg)
.padding(8)
.onAppear
if self.stream.messages.count > 0
scrollView.scrollTo(self.stream.messages.last!.id, anchor: .bottom)
.onChange(of: self.stream.messages, perform: inMessages in
if inMessages.count < 1 return
withAnimation(.linear(duration: 0.25))
scrollView.scrollTo(inMessages.last!.id, anchor: .bottom)
)
【问题讨论】:
更新帖子以显示代码。 【参考方案1】:您可以根据内容高度与整个滚动视图高度之间的差异使用动态大小的Spacer
。如果内容较小,则显示Spacer
-- 如果不是,则仅显示内容。
struct ContentView: View
@State private var contentHeight : CGFloat = 0
@State private var totalHeight : CGFloat = 0
var body: some View
ScrollViewReader scroller in
ScrollView
if contentHeight < totalHeight
Spacer().frame(height: totalHeight - contentHeight)
VStack
Text("My content")
Text("More content")
.frame(maxWidth: .infinity)
.background(GeometryReader
Color.clear.preference(key: ContentHeight.self,
value: $0.frame(in: .local).size.height)
)
.onPreferenceChange(ContentHeight.self) contentHeight = $0
.background(GeometryReader
Color.clear.preference(key: TotalHeight.self,
value: $0.frame(in: .local).size.height)
)
.border(Color.red)
.onPreferenceChange(TotalHeight.self) totalHeight = $0
struct TotalHeight : PreferenceKey
static var defaultValue: CGFloat 0
static func reduce(value: inout Value, nextValue: () -> Value)
value = value + nextValue()
struct ContentHeight : PreferenceKey
static var defaultValue: CGFloat 0
static func reduce(value: inout Value, nextValue: () -> Value)
value = value + nextValue()
【讨论】:
这回答了你的问题吗? 还没有机会尝试,但我会尝试的!不过,我想我会提交一个错误,请求对此提供内置支持,因为这是一项相当多的工作! 好的。如果答案有帮助,点赞总是好的。【参考方案2】:我对这个答案并不完全满意,因为它需要一些硬编码,并且不像 ScrollView
中的直接支持那样优雅,只允许滚动到内容的末尾,但它相当简单。
@jnpdx 的答案在正确的轨道上。
在我发布这个问题后的几周内,我学到了更多的 SwiftUI 技术。在这种情况下,您可以在 ScrollView
内部和外部添加几乎任意的填充和视图,包括列表/堆栈/任何内容(但不能在 ForEach
内部,这不会做你想要的!)。
我将ScrollViewReader
包裹在GeometryReader
中,以使整体聊天视图高度可用于内部视图,然后我在LazyVStack
顶部添加了等于高度减去硬编码量等于的填充到一行聊天的高度(这是我不喜欢的硬编码部分)。
struct OverlayChatView: View
@ObservedObject public var stream: ChatStream
var body: some View
GeometryReader
inChatView in
ScrollViewReader scrollView in
ScrollView
LazyVStack(alignment: .leading, spacing: 0.0)
ForEach(self.stream.messages) inMsg in
ChatMessageCell(message: inMsg)
.padding(.top, inChatView.size.height - 26.0)
.padding([.horizontal, .bottom], 8)
.onAppear
if self.stream.messages.count > 0
scrollView.scrollTo(self.stream.messages.last!.id, anchor: .bottom)
.onChange(of: self.stream.messages, perform: inMessages in
if inMessages.count < 1 return
withAnimation(.linear(duration: 0.25))
scrollView.scrollTo(inMessages.last!.id, anchor: .bottom)
)
这提供了ScrollView
需要让您一直向下滚动的额外内容。
【讨论】:
以上是关于SwiftUI 将部分列表固定到底部的主要内容,如果未能解决你的问题,请参考以下文章
SwiftUI 向视图添加 -y 偏移量,但将视图拉伸到底部
SwiftUI - ScrollView 没有一直滚动到底部
SwiftUI不借助ScrollViewReader和ScrollViewProxy实现List自动滚动到底部