Pociągnij w dół, aby odświeżyć dane w SwiftUI
Użyłem prostej listy danych za pomocą List
. Chciałbym dodać pull down, aby odświeżyć funkcjonalność, ale nie jestem pewien, które jest najlepsze możliwe podejście.
Pociągnij w dół, aby odświeżyć widok będzie widoczny tylko wtedy, gdy użytkownik spróbuje pociągnąć w dół z pierwszego indeksu, tak jak to zrobiliśmy w UITableView
z UIRefreshControl
w UIKit
Oto prosty kod do wyświetlania danych w 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"))
}
}
}
6 answers
Potrzebowałem tego samego do aplikacji, z którą się bawię, i wygląda na to, że SwiftUI API nie zawiera możliwości odświeżania dla ScrollView
s w tej chwili.
Z czasem API będzie rozwijało i naprawiało tego typu sytuacje, ale ogólnym rozwiązaniem problemu braku funkcjonalności w SwiftUI zawsze będzie implementacja struktury implementującej UIViewRepresentable
. Oto szybki i brudny dla UIScrollView
z kontrolką odświeżania.
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()
}
}
}
Ale oczywiście, nie możesz użyć żadnego SwiftUI komponenty w widoku przewijania bez zawijania ich w UIHostingController
i upuszczania w makeUIView
, zamiast umieszczania ich w LegacyScrollView() { // views here }
.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-06-11 14:21:31
Oto implementacja, która introspektuje hierarchię widoku i dodaje odpowiednie UIRefreshControl
do widoku tabeli listy SwiftUI: https://github.com/timbersoftware/SwiftUIRefresh
Większość logiki introspekcji można znaleźć tutaj: https://github.com/timbersoftware/SwiftUIRefresh/blob/15d9deed3fec66e2c0f6fd1fd4fe966142a891db/Sources/PullToRefresh.swift#L39-L73
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-11-26 02:04:18
Oto proste, małe i czyste rozwiązanie SwiftUI zrobiłem w celu dodania funkcji pull do odświeżania do widoku przewijania.
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)
}
}
Aby go użyć, wystarczy dodać go u góry widoku przewijania i nadać mu przestrzeń współrzędnych widoku przewijania:
ScrollView {
PullToRefresh(coordinateSpaceName: "pullToRefresh") {
// do your stuff when pulled
}
Text("Some view...")
}.coordinateSpace(name: "pullToRefresh")
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-12-02 01:25:15
Cześć, sprawdź tę bibliotekę, którą zrobiłem: https://github.com/AppPear/SwiftUI-PullToRefresh
Możesz zaimplementować go za pomocą jednej linii kodu:
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
}
}
}
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-09-19 15:27:53
Swiftui-introspects nie jest jeszcze obsługiwany na masOS, więc jeśli zamierzasz zbudować interfejs, który działa zarówno na iOS, jak i macOS, rozważ bibliotekę Samu Andras.
Rozwidlałem jego kod, dodałem kilka ulepszeń i dodałem możliwość korzystania bez widoku nawigacyjnego
Oto przykładowy kod.
RefreshableList(showRefreshView: $showRefreshView, action:{
// Your refresh action
// Remember to set the showRefreshView to false
self.showRefreshView = false
}){
ForEach(self.numbers, id: \.self){ number in
VStack(alignment: .leading){
Text("\(number)")
Divider()
}
}
}
Aby uzyskać więcej informacji, odwiedź poniższy link. https://github.com/phuhuynh2411/SwiftUI-PullToRefresh
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-04-06 04:31:57
Próbowałem wielu różnych rozwiązań, ale nic nie działało wystarczająco dobrze dla mojego przypadku.
GeometryReader
rozwiązania oparte miały złą wydajność dla złożonego układu.
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)
}
}
Po wejściu do ekranu lub wyjściu z niego, wyświetlane są znaki onAppear
i onDisappear
w widoku wyzwalacza Color.clear
.
Odświeżanie jest wyzwalane, jeśli czas pomiędzy pojawieniem się i zniknięciem widoku wyzwalacza jest większy niż minRefreshTimeInterval
, aby umożliwić odbicie ScrollView
bez wyzwalania odświeżania.
Aby go użyć Dodaj PullToRefreshView
do góry ScrollView
:
import SwiftUI
struct RefreshableScrollableContent: View
{
var body: some View
{
ScrollView
{
VStack(spacing: 0)
{
PullToRefreshView { print("refreshing") }
// ScrollView content
}
}
}
}
Gist: https://gist.github.com/tkashkin/e5f6b65b255b25269d718350c024f550
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2021-02-09 17:41:32