Created
March 27, 2020 11:29
-
-
Save beliy/1cee5d5aa8ce83357c822d0fcdaf7f48 to your computer and use it in GitHub Desktop.
SwiftUI - Pull to Refresh
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// ContentView.swift | |
// PullToRefresh | |
// | |
// Created by Alexey Belousov on 20.03.2020. | |
// Copyright © 2020 AskJack. All rights reserved. | |
// | |
import SwiftUI | |
import Combine | |
private let kFirstNames = [ | |
"James", "John", "Robert", "Michael", "William", | |
"David", "Richard", "Joseph", "Charles", "Thomas", | |
] | |
private let kLastNames = [ | |
"Adams", "Allen", "Anderson", "Armstrong", "Atkinson", | |
"Bailey", "Baker", "Ball", "Barker", "Barnes", | |
] | |
struct Record: Identifiable { | |
let id: Int | |
let name: String | |
let amount: Int | |
} | |
struct Generator { | |
static let `default` = Generator() | |
private func randomName() -> String { | |
[ | |
kFirstNames.randomElement(), | |
kLastNames.randomElement() | |
].compactMap { $0 } | |
.joined(separator: " ") | |
} | |
public func generate(_ count: Int = 10) -> AnyPublisher<[Record], Never> { | |
Just((0..<count).map { | |
Record(id: $0 + 1, name: randomName(), amount: Int.random(in: 50..<500)) | |
}).setFailureType(to: Never.self) | |
.eraseToAnyPublisher() | |
} | |
} | |
struct ContentView: View { | |
@State private var records: [Record] = [] | |
@State private var disposables = Set<AnyCancellable>() | |
private let generator: Generator = .default | |
var body: some View { | |
NavigationView { | |
ZStack { | |
ScrollViewAppearance(action: pullToRefresh) { | |
ZStack { | |
Color.red.opacity(0.05) | |
.edgesIgnoringSafeArea(.bottom) | |
ScrollView { | |
VStack(spacing: 8) { | |
ForEach(records) { record in | |
HStack { | |
VStack(alignment: .leading, spacing: 8) { | |
HStack { | |
Text("#\(record.id)") | |
Text(record.name) | |
}.font(.headline) | |
Text("Amount: \(record.amount)") | |
.font(.callout) | |
} | |
Spacer() | |
}.padding() | |
.background(Color.white) | |
.cornerRadius(10) | |
.shadow(radius: 5) | |
} | |
}.padding() | |
Divider() | |
.hidden() | |
} | |
} | |
}.edgesIgnoringSafeArea(.bottom) | |
}.navigationBarTitle("Pull to Refresh", displayMode: .inline) | |
.onAppear(perform: fetch) | |
} | |
} | |
} | |
extension ContentView { | |
private func fetch() { | |
generator.generate() | |
.receive(on: RunLoop.main) | |
.assign(to: \.records, on: self) | |
.store(in: &disposables) | |
} | |
private func pullToRefresh(refreshControl: UIRefreshControl) { | |
print(#function) | |
generator.generate() | |
.receive(on: RunLoop.main) | |
.sink { records in | |
self.records = records | |
refreshControl.endRefreshing() | |
} | |
.store(in: &disposables) | |
} | |
} | |
struct ContentView_Previews: PreviewProvider { | |
static var previews: some View { | |
ContentView() | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// ScrollViewAppearance.swift | |
// PullToRefresh | |
// | |
// Created by Alexey Belousov on 20.03.2020. | |
// Copyright © 2020 AskJack. All rights reserved. | |
// | |
import SwiftUI | |
import Combine | |
struct ScrollViewAppearance<Content>: UIViewControllerRepresentable where Content: View { | |
typealias UIViewControllerType = UIHostingController<Content> | |
class Coordinator: NSObject { | |
private let action: (UIRefreshControl) -> Void | |
init(action: @escaping (UIRefreshControl) -> Void) { | |
self.action = action | |
} | |
@objc func handleRefreshControl(sender: UIRefreshControl) { | |
action(sender) | |
} | |
} | |
private let action: (UIRefreshControl) -> Void | |
private let content: Content | |
init(action handler: @escaping (UIRefreshControl) -> Void, @ViewBuilder content builder: () -> Content) { | |
action = handler | |
content = builder() | |
} | |
func makeCoordinator() -> Coordinator { | |
Coordinator(action: action) | |
} | |
func makeUIViewController(context: Context) -> UIHostingController<Content> { | |
let scrollView = UIScrollView.appearance(whenContainedInInstancesOf: [UIViewControllerType.self]) | |
scrollView.refreshControl = UIRefreshControl() | |
scrollView.refreshControl?.addTarget(context.coordinator, action: #selector(Coordinator.handleRefreshControl(sender:)), for: .valueChanged) | |
return UIHostingController(rootView: content) | |
} | |
func updateUIViewController(_ uiViewController: UIHostingController<Content>, context: Context) { | |
uiViewController.rootView = content | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment