Cela peut être réalisé sur macOS en enveloppant un NSScrollView dans un objet NSViewControllerRepresentable (et je suppose que la même chose fonctionne sur iOS en utilisant UIScrollView et UIViewControllerRepresentable). Je pense que cela peut être un peu plus fiable que l'autre réponse ici puisque le système d'exploitation gère toujours une grande partie de la fonction du contrôle.
Je viens juste de le faire fonctionner, et je prévois d'essayer de faire fonctionner d'autres choses, comme obtenir la position de certaines lignes dans mon contenu, mais voici mon code jusqu'à présent :
import SwiftUI
struct ScrollableView<Content:View>: NSViewControllerRepresentable {
typealias NSViewControllerType = NSScrollViewController<Content>
var scrollPosition : Binding<CGPoint?>
var hasScrollbars : Bool
var content: () -> Content
init(hasScrollbars: Bool = true, scrollTo: Binding<CGPoint?>, @ViewBuilder content: @escaping () -> Content) {
self.scrollPosition = scrollTo
self.hasScrollbars = hasScrollbars
self.content = content
}
func makeNSViewController(context: NSViewControllerRepresentableContext<Self>) -> NSViewControllerType {
let scrollViewController = NSScrollViewController(rootView: self.content())
scrollViewController.scrollView.hasVerticalScroller = hasScrollbars
scrollViewController.scrollView.hasHorizontalScroller = hasScrollbars
return scrollViewController
}
func updateNSViewController(_ viewController: NSViewControllerType, context: NSViewControllerRepresentableContext<Self>) {
viewController.hostingController.rootView = self.content()
if let scrollPosition = self.scrollPosition.wrappedValue {
viewController.scrollView.contentView.scroll(scrollPosition)
DispatchQueue.main.async(execute: {self.scrollPosition.wrappedValue = nil})
}
viewController.hostingController.view.frame.size = viewController.hostingController.view.intrinsicContentSize
}
}
class NSScrollViewController<Content: View> : NSViewController, ObservableObject {
var scrollView = NSScrollView()
var scrollPosition : Binding<CGPoint>? = nil
var hostingController : NSHostingController<Content>! = nil
@Published var scrollTo : CGFloat? = nil
override func loadView() {
scrollView.documentView = hostingController.view
view = scrollView
}
init(rootView: Content) {
self.hostingController = NSHostingController<Content>(rootView: rootView)
super.init(nibName: nil, bundle: nil)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
}
}
struct ScrollableViewTest: View {
@State var scrollTo : CGPoint? = nil
var body: some View {
ScrollableView(scrollTo: $scrollTo)
{
Text("Scroll to bottom").onTapGesture {
self.$scrollTo.wrappedValue = CGPoint(x: 0,y: 1000)
}
ForEach(1...50, id: \.self) { (i : Int) in
Text("Test \(i)")
}
Text("Scroll to top").onTapGesture {
self.$scrollTo.wrappedValue = CGPoint(x: 0,y: 0)
}
}
}
}