I have the following code, but I am not sure where to place it in my UIViewRepresntable. Any suggestions?
let scrollTo = IndexPath(row: 25, section: 0)
view.scrollToItem(at: scrollTo, at: .top, animated: false)
struct CollectionViewRepresentable: UIViewRepresentable {
@StateObject var vm = CollectionViewModel()
let view = UICollectionView(frame: .zero, collectionViewLayout: UICollectionViewCompositionalLayout.list(using: UICollectionLayoutListConfiguration(appearance: .plain)))
func makeUIView(context: Context) -> UICollectionView {
view.backgroundColor = UIColor.clear
view.dataSource = context.coordinator
view.delegate = context.coordinator
view.register(ListCell.self, forCellWithReuseIdentifier: "listCell")
return view
}
func updateUIView(_ uiView: UICollectionView, context: Context) {
}
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
class Coordinator: NSObject, UICollectionViewDelegate, UICollectionViewDataSource {
private var parent: CollectionViewRepresentable
init(_ collectionViewRepresentable: CollectionViewRepresentable) {
self.parent = collectionViewRepresentable
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return parent.vm.items.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "listCell", for: indexPath) as! ListCell
let item = parent.vm.items[indexPath.row]
cell.item = item
return cell
}
}
}
I need to scroll to that position when the view appears.
CodePudding user response:
A scenario is not completely clear, but it is possible to use external State
and a Binding
inside representable, changing with calls updateUIView
.
So a possible solution is
@State private var scrollTo = IndexPath(row: 25, section: 0) // << in parent !!
// ...
struct CollectionViewRepresentable: UIViewRepresentable {
@Binding var scrollTo: IndexPath // << 1) !!
@StateObject private var vm = CollectionViewModel()
func makeUIView(context: Context) -> UICollectionView {
let view = UICollectionView(...) // << move creation here !!
// ... other code
return view
}
func updateUIView(_ uiView: UICollectionView, context: Context) {
// called at start and when binding updated
uiView.scrollToItem(at: scrollTo, at: .top, animated: false) // << 2) !!
}
// ... other code
}
CodePudding user response:
You could use ViewControllerRepresentable
and take advantage of the fact that view controllers can override the viewWillAppear
/viewDidAppear
methods where you can write the code that scrolls.
For this, subclass UICollectionViewController
, and move all the collection view related logic there:
class MyCollectionViewController: UICollectionViewController {
var vm = CollectionViewModel()
override func viewDidLoad() {
super.viewDidLoad()
collectionView.backgroundColor = UIColor.clear
collectionView.register(ListCell.self, forCellWithReuseIdentifier: "listCell")
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
let scrollTo = IndexPath(row: 25, section: 0)
collectionView.scrollToItem(at: scrollTo, at: .top, animated: false)
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return vm.items.count
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "listCell", for: indexPath) as! ListCell
let item = vm.items[indexPath.row]
cell.item = item
return cell
}
}
With the above in mind, the SwiftUI code simplifies to something like this:
struct CollectionViewRepresentable: UIViewControllerRepresentable {
func makeUIViewController(context: Context) -> MyCollectionViewController {
.init(collectionViewLayout: UICollectionViewFlowLayout())
}
func updateUIViewController(_ vc: MyCollectionViewController, context: Context) {
}
}