简体   繁体   中英

How to reload collectionview in UIViewRepresentable SwiftUI

## How to reload collection view in UIViewRepresentable ##

Working with UIViewRepresentable and collection view, got stuck when its comes to reload() collection view after iterating, how to reload collection view in UIViewRepresentable when performing iterate through data? func updateUIView doesn't do the work.


    struct VideoCollectionView: UIViewRepresentable {

        var data: VideoViewModel

        @Binding var search: String

        var dataSearch: [VideoPostModel] {
            if search.isEmpty {
                return data.postsSearch
            }else{
                let d = data.postsSearch.filter {$0.artistname.localizedStandardContains(search)}
                return d
            }
        }

        var didSelectItem: ((_ indexPath: IndexPath)->()) = {_ in }
        var didSelectObject: ((_ boject: VideoPostModel)->()) = {_ in }

        func makeUIView(context: Context) -> UICollectionView {
            let reuseId = "AlbumPrivateCell"

            let collection :UICollectionView = {
                let layout = UICollectionViewFlowLayout()
                layout.sectionHeadersPinToVisibleBounds = true
                let collectionV = UICollectionView(frame: .zero, collectionViewLayout: layout)
                layout.scrollDirection = .vertical
                collectionV.translatesAutoresizingMaskIntoConstraints = false
                collectionV.backgroundColor = .clear
                collectionV.dataSource = context.coordinator
                collectionV.delegate = context.coordinator
                collectionV.register(AlbumPrivateCell.self, forCellWithReuseIdentifier: reuseId)

                return collectionV
            }()

            return collection
        }
        func updateUIView(_ collectionView: UICollectionView, context: UIViewRepresentableContext<VideoCollectionView>) {
            print("updateUIView updateUIView")
            print(search)

            collectionView.reloadData() 
        }
        func makeCoordinator() -> Coordinator {
            Coordinator(self)
        }

        class Coordinator: NSObject, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {

            private var parent: VideoCollectionView
            init(_ albumGridView: VideoCollectionView) {
                self.parent = albumGridView
            }
            func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
                return self.parent.dataSearch.count
            }
            func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
                let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "AlbumPrivateCell", for: indexPath) as! AlbumPrivateCell
                    cell.data = self.parent.dataSearch[indexPath.item]
                return cell
            }
            func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
                let post = self.parent.dataSearch[indexPath.item]
                parent.didSelectItem(indexPath)
                parent.didSelectObject(post)
            }
            func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
                let width = collectionView.frame.width
                let height = collectionView.frame.height/2
                return CGSize(width: width, height: height)
            }

        }
    }

Dealing with this right now, only way i was able to make a reloadData, was creating a reference inside my Coordinator class and pass it on creation:

context.coordinator.collectionView = collectionView

From there you can make the call to reloadData(), pretty sure there are more elegant ways to work this though.

EDIT: Answering Request to expand on my approach.

Let's say you have a coordinator like this:

class CoordinatorItemPicturesView: NSObject, UICollectionViewDataSource, UICollectionViewDelegate {
// MARK: - Properties
var collectionView: UICollectionView!
var elements = [String]()
init(elements:[String]) {
    self.elements = elements
}

// MARK: UICollectionViewDataSource

func collectionView(_: UICollectionView, numberOfItemsInSection _: Int) -> Int {
    return elements.count
}

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    return  UICollectionViewCell()
}
}

So when you create your UIViewRepresentable, pass the collectionView reference:

struct ItemPicturesView: UIViewRepresentable {
func makeUIView(context: UIViewRepresentableContext<ItemPicturesView>) -> UICollectionView {
    /// Set Context, cells, etc
    let collectionView = UICollectionView(frame: .zero, collectionViewLayout: UICollectionViewFlowLayout())
    collectionView.backgroundColor = .clear
    collectionView.translatesAutoresizingMaskIntoConstraints = false
    collectionView.dataSource = context.coordinator
    collectionView.delegate = context.coordinator
    collectionView.isScrollEnabled = true
    // --- Right here
    context.coordinator.collectionView = collectionView
    return collectionView
}

func updateUIView(_ uiView: UICollectionView, context _: Context) {
    UIView.animate(withDuration: 1.0) {
        uiView.frame = uiView.frame
        uiView.reloadData()
    }
}

func makeCoordinator() -> CoordinatorItemPicturesView {
    CoordinatorItemPicturesView(elements:["Element One", "Element Two"])
}
}

I'm sorry, if the code is not 100% perfect, had to take it from a project with NDA and didn't test with the deleted properties.

UIViewRepresentable is just a wrapper. you have to make ur model as Observable. then it will automatically update whenever any change in data.

All you need to do is, make your data property in VideoCollectionView as:

@Binding var data: VideoViewModel

Now once you update data, you update view will reload collectionView. Explanation: Your data property(value type) should be a Binding property as the Coordinator depends on that. When you init Coordinator with self, you are passing the old instance of data property. Changing it to binding allow it to be mutated.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM