当我使用List视图时,我可以轻松地添加refreshable
修饰符来触发刷新逻辑。我的问题是如何在使用LazyVStack时实现相同的目标。
我有以下代码:
struct TestListView: View {
var body: some View {
Text("the list view")
// WORKS:
// VStack {
// List {
// ForEach(0..<10) { n in
// Text("N = \(n)")
// }
// }
// .refreshable {
//
// }
// }
// DOES NOT SHOW REFRESH CONTROL:
ScrollView {
LazyVStack {
ForEach(0..<10) { n in
Text("N = \(n)")
}
}
}
.refreshable {
}
}
}
在LazyVStack
情况下,如何获得刷新行为的拉力?
发布于 2022-05-31 20:05:15
事实上,这是可能的,我想说,我理解苹果的想法--他们给重型List
默认内置的行为--但是让轻量级的ScrollView
做好准备,这样我们就可以用我们需要的任何方式来定制它。
下面是解决方案的演示(用Xcode 13.4 / iOS 15.5测试)
主要部分:
struct ContentView: View {
var body: some View {
ScrollView {
RefreshableView()
}
.refreshable { // << injects environment value !!
await fetchSomething()
}
}
}
struct RefreshableView: View {
@Environment(\.refresh) private var refresh // << refreshable injected !!
@State private var isRefreshing = false
var body: some View {
VStack {
if isRefreshing {
MyProgress()
.transition(.scale)
}
// ...
.onPreferenceChange(ViewOffsetKey.self) {
if $0 < -80 && !isRefreshing { // << any creteria we want !!
isRefreshing = true
Task {
await refresh?() // << call refreshable !!
await MainActor.run {
isRefreshing = false
}
}
}
}
发布于 2022-09-22 09:50:56
现在,SwiftUI向ScrollView
添加了.refreshable
修饰符。
只要用List
的方式就行了
ScrollView {
LazyVStack {
// Loop and add View
}
}
.refreshable {
refreshLogic()
}
不过,它支持启动iOS 15。
这里是文档参考
@available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *)
extension View {
/// Marks this view as refreshable.
public func refreshable(action: @escaping @Sendable () async -> Void) -> some View
}
发布于 2022-07-06 13:36:46
基于Asperi的答案:
import SwiftUI
struct ContentView: View {
var body: some View {
ScrollView {
RefreshableView {
RoundedRectangle(cornerRadius: 20)
.fill(.red).frame(height: 100).padding()
.overlay(Text("Button"))
.foregroundColor(.white)
}
}
.refreshable { // << injects environment value !!
await fetchSomething()
}
}
func fetchSomething() async {
// demo, assume we update something long here
try? await Task.sleep(nanoseconds: 2 * 1_000_000_000)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
struct RefreshableView<Content: View>: View {
var content: () -> Content
@Environment(\.refresh) private var refresh // << refreshable injected !!
@State private var isRefreshing = false
var body: some View {
VStack {
if isRefreshing {
MyProgress() // ProgressView() ?? - no, it's boring :)
.transition(.scale)
}
content()
}
.animation(.default, value: isRefreshing)
.background(GeometryReader {
// detect Pull-to-refresh
Color.clear.preference(key: ViewOffsetKey.self, value: -$0.frame(in: .global).origin.y)
})
.onPreferenceChange(ViewOffsetKey.self) {
if $0 < -80 && !isRefreshing { // << any creteria we want !!
isRefreshing = true
Task {
await refresh?() // << call refreshable !!
await MainActor.run {
isRefreshing = false
}
}
}
}
}
}
struct MyProgress: View {
@State private var isProgress = false
var body: some View {
HStack{
ForEach(0...4, id: \.self){index in
Circle()
.frame(width:10,height:10)
.foregroundColor(.red)
.scaleEffect(self.isProgress ? 1:0.01)
.animation(self.isProgress ? Animation.linear(duration:0.6).repeatForever().delay(0.2*Double(index)) :
.default
, value: isProgress)
}
}
.onAppear { isProgress = true }
.padding()
}
}
public struct ViewOffsetKey: PreferenceKey {
public typealias Value = CGFloat
public static var defaultValue = CGFloat.zero
public static func reduce(value: inout Value, nextValue: () -> Value) {
value += nextValue()
}
}
https://stackoverflow.com/questions/70610403
复制相似问题