Pin*_*Gjr 9 list uirefreshcontrol swift swiftui
我已经使用列出了简单的数据列表List
。我想添加下拉菜单以刷新功能,但是我不确定哪种方法是最好的。
下拉刷新视图才可以看到,当用户尝试从同一像我们的第一个指数下拉在做UITableView
与UIRefreshControl
在UIKit
这是在中列出数据的简单代码SwiftUI
。
struct CategoryHome: View {
var categories: [String: [Landmark]] {
.init(
grouping: landmarkData,
by: { $0.category.rawValue }
)
}
var body: some View {
NavigationView {
List {
ForEach(categories.keys.sorted().identified(by: \.self)) { key in
Text(key)
}
}
.navigationBarTitle(Text("Featured"))
}
}
}
Run Code Online (Sandbox Code Playgroud)
小智 20
这是我制作的一个简单、小巧且纯SwiftUI 的解决方案,以便向 ScrollView 添加下拉刷新功能。
struct PullToRefresh: View {
var coordinateSpaceName: String
var onRefresh: ()->Void
@State var needRefresh: Bool = false
var body: some View {
GeometryReader { geo in
if (geo.frame(in: .named(coordinateSpaceName)).midY > 50) {
Spacer()
.onAppear {
needRefresh = true
}
} else if (geo.frame(in: .named(coordinateSpaceName)).maxY < 10) {
Spacer()
.onAppear {
if needRefresh {
needRefresh = false
onRefresh()
}
}
}
HStack {
Spacer()
if needRefresh {
ProgressView()
} else {
Text("??")
}
Spacer()
}
}.padding(.top, -50)
}
}
Run Code Online (Sandbox Code Playgroud)
要使用它很简单,只需将它添加到 ScrollView 的顶部并为其指定 ScrollView 的坐标空间:
ScrollView {
PullToRefresh(coordinateSpaceName: "pullToRefresh") {
// do your stuff when pulled
}
Text("Some view...")
}.coordinateSpace(name: "pullToRefresh")
Run Code Online (Sandbox Code Playgroud)
ldi*_*ual 18
这是一个自省视图层次结构并向UIRefreshControl
SwiftUI 列表的表视图添加属性的实现:https : //github.com/timbersoftware/SwiftUIRefresh
大量内省逻辑可以在这里找到:https : //github.com/timbersoftware/SwiftUIRefresh/blob/15d9deed3fec66e2c0f6fd1fd4fe966142a891db/Sources/PullToRefresh.swift#L39-L73
Eva*_*ubl 12
我正在玩的应用程序需要同样的东西,而且看来SwiftUI API目前不包含的刷新控制功能ScrollView
。
随着时间的流逝,API将开发并纠正这种情况,但是SwiftUI中缺少功能的一般后备之处始终是实现一个实现的结构UIViewRepresentable
。这是一个快速且肮脏UIScrollView
的刷新控件。
struct LegacyScrollView : UIViewRepresentable {
// any data state, if needed
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
func makeUIView(context: Context) -> UIScrollView {
let control = UIScrollView()
control.refreshControl = UIRefreshControl()
control.refreshControl?.addTarget(context.coordinator, action:
#selector(Coordinator.handleRefreshControl),
for: .valueChanged)
// Simply to give some content to see in the app
let label = UILabel(frame: CGRect(x: 0, y: 0, width: 200, height: 30))
label.text = "Scroll View Content"
control.addSubview(label)
return control
}
func updateUIView(_ uiView: UIScrollView, context: Context) {
// code to update scroll view from view state, if needed
}
class Coordinator: NSObject {
var control: LegacyScrollView
init(_ control: LegacyScrollView) {
self.control = control
}
@objc func handleRefreshControl(sender: UIRefreshControl) {
// handle the refresh event
sender.endRefreshing()
}
}
}
Run Code Online (Sandbox Code Playgroud)
但是,当然,您不能在滚动视图中使用任何SwiftUI组件,除非将它们包装在中UIHostingController
并放进去makeUIView
,而不是将它们放到中LegacyScrollView() { // views here }
。
Ana*_*kin 11
我尝试了许多不同的解决方案,但没有一个适合我的情况。
GeometryReader
基于解决方案的复杂布局性能不佳。
这是一个纯 SwiftUI 2.0 视图,似乎运行良好,不会因持续状态更新而降低滚动性能,并且不使用任何 UIKit hacks:
import SwiftUI
struct PullToRefreshView: View
{
private static let minRefreshTimeInterval = TimeInterval(0.2)
private static let triggerHeight = CGFloat(100)
private static let indicatorHeight = CGFloat(100)
private static let fullHeight = triggerHeight + indicatorHeight
let backgroundColor: Color
let foregroundColor: Color
let isEnabled: Bool
let onRefresh: () -> Void
@State private var isRefreshIndicatorVisible = false
@State private var refreshStartTime: Date? = nil
init(bg: Color = .white, fg: Color = .black, isEnabled: Bool = true, onRefresh: @escaping () -> Void)
{
self.backgroundColor = bg
self.foregroundColor = fg
self.isEnabled = isEnabled
self.onRefresh = onRefresh
}
var body: some View
{
VStack(spacing: 0)
{
LazyVStack(spacing: 0)
{
Color.clear
.frame(height: Self.triggerHeight)
.onAppear
{
if isEnabled
{
withAnimation
{
isRefreshIndicatorVisible = true
}
refreshStartTime = Date()
}
}
.onDisappear
{
if isEnabled, isRefreshIndicatorVisible, let diff = refreshStartTime?.distance(to: Date()), diff > Self.minRefreshTimeInterval
{
onRefresh()
}
withAnimation
{
isRefreshIndicatorVisible = false
}
refreshStartTime = nil
}
}
.frame(height: Self.triggerHeight)
indicator
.frame(height: Self.indicatorHeight)
}
.background(backgroundColor)
.ignoresSafeArea(edges: .all)
.frame(height: Self.fullHeight)
.padding(.top, -Self.fullHeight)
}
private var indicator: some View
{
ProgressView()
.progressViewStyle(CircularProgressViewStyle(tint: foregroundColor))
.opacity(isRefreshIndicatorVisible ? 1 : 0)
}
}
Run Code Online (Sandbox Code Playgroud)
当进入或离开屏幕边界时,它使用LazyVStack
负填充来调用onAppear
和onDisappear
触发视图。Color.clear
如果触发视图出现和消失之间的时间大于minRefreshTimeInterval
允许ScrollView
弹跳而不触发刷新的时间,则会触发刷新。
要使用它,请添加PullToRefreshView
到以下内容的顶部ScrollView
:
import SwiftUI
struct RefreshableScrollableContent: View
{
var body: some View
{
ScrollView
{
VStack(spacing: 0)
{
PullToRefreshView { print("refreshing") }
// ScrollView content
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
要点:https://gist.github.com/tkashkin/e5f6b65b255b25269d718350c024f550
从 iOS 15+
NavigationView {
List(1..<100) { row in
Text("Row \(row)")
}
.refreshable {
print("write your pull to refresh logic here")
}
}
Run Code Online (Sandbox Code Playgroud)
更多详情:Apple Doc
嗨,看看我制作的这个库:https : //github.com/AppPear/SwiftUI-PullToRefresh
你可以通过一行代码来实现:
struct CategoryHome: View {
var categories: [String: [Landmark]] {
.init(
grouping: landmarkData,
by: { $0.category.rawValue }
)
}
var body: some View {
RefreshableNavigationView(title: "Featured", action:{
// your refresh action
}){
ForEach(categories.keys.sorted().identified(by: \.self)) { key in
Text(key)
Divider() // !!! this is important to add cell separation
}
}
}
}
}
Run Code Online (Sandbox Code Playgroud)