I'm using Introspect to apply pull to refresh on my ScrollView.
When I pull down to refresh the scroll view jumps back.
I know that this is a common problem that answered already but with SwiftUI and Introspect I don't know where to put the finger.
I know that it can be fixed with scrollDidEndDragging but I can't implement it here.
Does any one have an idea how to solve this issue with this given code?
The code:
private struct PullToRefresh: UIViewRepresentable {
@Binding var isShowing: Bool
let onRefresh: () -> Void
public init(isShowing: Binding<Bool>, onRefresh: @escaping () -> Void) {
_isShowing = isShowing
self.onRefresh = onRefresh
}
public class Coordinator {
let onRefresh: () -> Void
let isShowing: Binding<Bool>
init(onRefresh: @escaping () -> Void, isShowing: Binding<Bool>) {
self.onRefresh = onRefresh
self.isShowing = isShowing
}
@objc func onValueChanged() {
isShowing.wrappedValue = true
onRefresh()
}
}
public func makeUIView(context: UIViewRepresentableContext<PullToRefresh>) -> UIView {
let view = UIView(frame: .zero)
view.isHidden = true
view.isUserInteractionEnabled = false
return view
}
private func scrollView(entry: UIView) -> UIScrollView? {
if let scrollView = Introspect.findAncestor(ofType: UIScrollView.self, from: entry) {
return scrollView
}
guard let viewHost = Introspect.findViewHost(from: entry) else {
return nil
}
return Introspect.previousSibling(containing: UIScrollView.self, from: viewHost)
}
public func updateUIView(_ uiView: UIView, context: UIViewRepresentableContext<PullToRefresh>) {
DispatchQueue.main.asyncAfter(deadline: .now()) {
guard let scrollView = self.scrollView(entry: uiView) else {
return
}
if let refreshControl = scrollView.refreshControl {
if self.isShowing {
refreshControl.beginRefreshing()
} else {
refreshControl.endRefreshing()
}
return
}
let refreshControl = UIRefreshControl()
refreshControl.addTarget(context.coordinator, action: #selector(Coordinator.onValueChanged), for: .valueChanged)
scrollView.refreshControl = refreshControl
}
}
public func makeCoordinator() -> Coordinator {
return Coordinator(onRefresh: onRefresh, isShowing: $isShowing)
}
}
Usage:
extension View {
public func pullToRefresh(isShowing: Binding<Bool>, onRefresh: @escaping () -> Void) -> some View {
return overlay(
PullToRefresh(isShowing: isShowing, onRefresh: onRefresh)
.frame(width: 0, height: 0)
, alignment: .center)
}
}