首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >SwiftUI:为LazyVStack添加可刷新功能?

SwiftUI:为LazyVStack添加可刷新功能?
EN

Stack Overflow用户
提问于 2022-01-06 16:33:42
回答 4查看 3K关注 0票数 2

当我使用List视图时,我可以轻松地添加refreshable修饰符来触发刷新逻辑。我的问题是如何在使用LazyVStack时实现相同的目标。

我有以下代码:

代码语言:javascript
运行
复制
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情况下,如何获得刷新行为的拉力?

EN

回答 4

Stack Overflow用户

发布于 2022-05-31 20:05:15

事实上,这是可能的,我想说,我理解苹果的想法--他们给重型List默认内置的行为--但是让轻量级的ScrollView做好准备,这样我们就可以用我们需要的任何方式来定制它。

下面是解决方案的演示(用Xcode 13.4 / iOS 15.5测试)

主要部分:

代码语言:javascript
运行
复制
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
                    }
                }
            }
        }

完整的测试模块在这里

票数 8
EN

Stack Overflow用户

发布于 2022-09-22 09:50:56

现在,SwiftUI向ScrollView添加了.refreshable修饰符。

只要用List的方式就行了

代码语言:javascript
运行
复制
ScrollView {
    LazyVStack {
        // Loop and add View
    }
}
.refreshable {
    refreshLogic()
}

不过,它支持启动iOS 15。

这里是文档参考

代码语言:javascript
运行
复制
@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
}
票数 6
EN

Stack Overflow用户

发布于 2022-07-06 13:36:46

基于Asperi的答案:

代码语言:javascript
运行
复制
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()
    }
}
票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/70610403

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档