JohnOfIreland
JohnOfIreland

Reputation: 281

UICollectionView in UIViewRepresentable won't scrollToItem from onAppear

I have a class which is set up to trigger movement functions in a member variable UICollectionView :

class CollectionFunctionStore : ObservableObject {
    //class to hold data about a UICollection and from where some functions can be triggered
    var collectionRef : UICollectionView?

    func jumpGrid(){
        if let lclCollection = collectionRef {
        let ip = IndexPath(row: 15, section: 0)
        lclCollection.scrollToItem(at: ip, at: .bottom, animated: false)
        lclCollection.reloadData()
        print("jumpGrid() was reached: ")
        }
    }
}

the single function there "jumpGrid" will bounce the grid down to the 15th row this class is being used in a swiftUI app and the collection in question is contained in a UIViewRepresentable. Both the function store and the UIViewRepresentable containing the UICollectionView are in a struct along with a button in the top level content view

import SwiftUI

struct ContentView: View {
    @ObservedObject var swiperStore = CollectionFunctionStore()
    var body: some View {
       return ZStack {
        VStack{
            CollectionAndStoreContainer(swiperStore: swiperStore)
            JumpButton(swiperStore: swiperStore)
        }.onAppear(perform:  swiperStore.jumpGrid)
       }
    }
}

struct CollectionAndStoreContainer : View {
    @ObservedObject var swiperStore : CollectionFunctionStore
    var body: some View{
        return ZStack{
            CollectionViewRepresentable(swiperStoreParam: swiperStore).frame(width: 30, height: 200)
        }
    }
}

struct JumpButton : View {
    @ObservedObject var swiperStore : CollectionFunctionStore
    var body : some View{
        return Button(action: {
            swiperStore.jumpGrid()
        }){
            ZStack {
                Rectangle().frame(width: 60, height: 30).foregroundColor(.orange).cornerRadius(4)
                Text("j").foregroundColor(.white)
            }
        }
    }
}

The problem Im facing is this: the button which contains the same function store as the top level view can trigger the scroll to item function without any problem, whereas the onAppear event can call the function and it will actually print the message contained in its code but it does not move the content of the collection... Button calls the function and the UICollectionView responds properly onAppear calls the function but the UICollectionView does not move.

=====Some additional info===== I do not have a choice in the use of UIViewRepresentable in this situation, I cant use lazyHgrid or HStacks also here is the code of the UIViewRepresentable in question

struct CollectionViewRepresentable : UIViewRepresentable {
    
    var swiperStore : CollectionFunctionStore
    
    typealias UIViewType = UICollectionView
    
    init(swiperStoreParam : CollectionFunctionStore) {
        swiperStore = swiperStoreParam
    }
    
    func makeUIView(context: Context) -> UICollectionView {
        
        let layout = UICollectionViewFlowLayout()
        layout.scrollDirection = .vertical
        layout.itemSize = CGSize(width: 30, height: 30)
        layout.sectionInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
        layout.minimumLineSpacing = 0
       
        let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
        collectionView.dataSource = context.coordinator
        collectionView.delegate = context.coordinator
        collectionView.register(ExtendedCell.self, forCellWithReuseIdentifier: "ExtCell")
        collectionView.isScrollEnabled = true
        collectionView.showsHorizontalScrollIndicator = false
        collectionView.showsVerticalScrollIndicator = false
        swiperStore.collectionRef = collectionView
        return collectionView
    }
    
    func makeCoordinator() -> Coordinator {
            return Coordinator()
    }
    
    func updateUIView(_ uiView: UICollectionView, context: Context) {
         
    }
}

anyone any ideas? The overall goal is to get the UICollectionView to scroll to a specific location at the very start of its appearance so I thought that using onAppear in SwiftUI was the best way to go, but for some reason it's not moving the grid, is there even a better option in the Coordinator for example? Any help appreciated..

Upvotes: 1

Views: 698

Answers (1)

Asperi
Asperi

Reputation: 258413

I think it is too early to call scroll-to in onAppear, because collection view has not finished construction yet at that time.

Try

}.onAppear {
 DispatchQueue.main.async { swiperStore.jumpGrid() }
}

or even

}.onAppear {
 // with some delay, if appeared constructed with animation or async, etc.
 DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {  // 0.25-0.5 sec
    swiperStore.jumpGrid() 
 }
}

Upvotes: 1

Related Questions