Created
April 29, 2020 23:17
-
-
Save eito/144ce4fd80bda6ed03551a5763a18e2f to your computer and use it in GitHub Desktop.
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
import SwiftUI | |
struct PullToRefreshSample: View { | |
let items = [ | |
"one", "two", "three", "four", "five" | |
] | |
@State private var isRefreshing = true | |
var body: some View { | |
List(items, id: \.self) { item in | |
Text(item) | |
} | |
.navigationBarTitle("My List") | |
.pullToRefresh(isRefreshing: $isRefreshing) { | |
print("refreshing...") | |
DispatchQueue.main.asyncAfter(deadline: .now() + 2) { | |
print("done...") | |
self.isRefreshing = false | |
} | |
} | |
} | |
} | |
struct RefreshControl: UIViewRepresentable { | |
@Binding var isRefreshing: Bool | |
let refreshHandler: () -> Void | |
init(isRefreshing: Binding<Bool>, refreshHandler: @escaping () -> Void) { | |
_isRefreshing = isRefreshing | |
self.refreshHandler = refreshHandler | |
} | |
class Coordinator { | |
let isRefreshing: Binding<Bool> | |
let refreshHandler: () -> Void | |
init(isRefreshing: Binding<Bool>, refreshHandler: @escaping () -> Void) { | |
self.isRefreshing = isRefreshing | |
self.refreshHandler = refreshHandler | |
} | |
@objc | |
func refresh() { | |
isRefreshing.wrappedValue = true | |
refreshHandler() | |
} | |
} | |
func makeCoordinator() -> Coordinator { | |
Coordinator(isRefreshing: $isRefreshing, refreshHandler: refreshHandler) | |
} | |
func makeUIView(context: UIViewRepresentableContext<RefreshControl>) -> UIView { | |
let view = UIView(frame: .zero) | |
view.isHidden = true | |
view.isUserInteractionEnabled = false | |
return view | |
} | |
func updateUIView(_ uiView: UIView, context: UIViewRepresentableContext<RefreshControl>) { | |
// this gets called a lot early, without the dispatch it doesn't work | |
// TODO - find out why | |
// | |
DispatchQueue.main.asyncAfter(deadline: .now()) { | |
guard let tableView = self.findTableView(from: uiView) else { | |
return | |
} | |
if let refreshControl = tableView.refreshControl { | |
if self.isRefreshing { | |
refreshControl.beginRefreshing() | |
} else { | |
refreshControl.endRefreshing() | |
} | |
return | |
} | |
let refreshControl = UIRefreshControl() | |
refreshControl.addTarget(context.coordinator, action: #selector(Coordinator.refresh), for: .valueChanged) | |
tableView.refreshControl = refreshControl | |
} | |
} | |
private func findTableView(from view: UIView) -> UITableView? { | |
var tableView: UITableView? | |
if let tv = view.findChild(ofType: UITableView.self) { | |
tableView = tv | |
} else if let tv = view.findChildInAncestors(ofType: UITableView.self) { | |
tableView = tv | |
} else if let tv = view.findChildInSiblings(ofType: UITableView.self) { | |
tableView = tv | |
} | |
return tableView | |
} | |
} | |
extension UIView { | |
func findChild<T>(ofType type: T.Type) -> T? { | |
for subview in subviews { | |
if let t = subview as? T { | |
return t | |
} | |
} | |
for subview in subviews { | |
if let foundInChild = subview.findChild(ofType: type) { | |
return foundInChild | |
} | |
} | |
return nil | |
} | |
func findChild<T>(ofType type: T.Type, in view: UIView) -> T? { | |
return view.findChild(ofType: type) | |
} | |
func findChildInAncestors<T>(ofType type: T.Type) -> T? { | |
var sv = superview | |
while let s = sv { | |
if let t = s.findChildInSiblings(ofType: type) { | |
return t | |
} | |
sv = s.superview | |
} | |
return nil | |
} | |
func findChildInSiblings<T>(ofType type: T.Type) -> T? { | |
guard let superview = superview else { return nil } | |
for sibling in superview.subviews.filter( { $0 != self } ).reversed() { | |
if let t = sibling.findChild(ofType: type) { | |
return t | |
} | |
} | |
return nil | |
} | |
} | |
extension View { | |
public func pullToRefresh(isRefreshing: Binding<Bool>, refreshHandler: @escaping () -> Void) -> some View { | |
return overlay( | |
RefreshControl(isRefreshing: isRefreshing, refreshHandler: refreshHandler) | |
.frame(width: 0, height: 0) | |
) | |
} | |
} | |
struct PullToRefreshSample_Previews: PreviewProvider { | |
static var previews: some View { | |
PullToRefreshSample() | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment