zum*_*zum 7 ios swiftui lazyvstack
当我使用列表视图时,我可以轻松添加修饰符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 {
}
}
}
Run Code Online (Sandbox Code Playgroud)
在这种情况下如何获得刷新行为LazyVStack?
Asp*_*eri 12
实际上这是可能的,我想说,我理解苹果的想法 - 他们为 Heavy 提供了默认的内置行为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
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
Wis*_*ssa 10
适用于 iOS 16+
现在 SwiftUI 添加了.refreshable修饰符到ScrollView.
只需按照您的方式使用即可List
ScrollView {
LazyVStack {
// Loop and add View
}
}
.refreshable {
refreshLogic()
}
Run Code Online (Sandbox Code Playgroud)
这是文档参考
@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
}
Run Code Online (Sandbox Code Playgroud)
小智 6
基于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()
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
9021 次 |
| 最近记录: |