web-dev-qa-db-fra.com

Comment définir une vue SwiftUI en tant que cellule sur une CollectionView

J'ai trouvé que les vues SwiftUI Text étaient extrêmement faciles à créer des étiquettes avec des conceptions personnalisées. Je voulais donc l'utiliser comme vue d'une UIKit UICollectionViewCell standard.

C'est mon code jusqu'à présent (vous pouvez copier et coller dans Xcode 11).

import SwiftUI
import UIKit

struct ContentView: View {
    var body: some View {
        CollectionComponent()
    }
}

#if DEBUG
struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
#endif

struct CollectionComponent : UIViewRepresentable {
    func makeCoordinator() -> CollectionComponent.Coordinator {
        Coordinator(data: [])
    }

    class Coordinator: NSObject, UICollectionViewDataSource, UICollectionViewDelegate {
        var data: [String] = []

        init(data: [String]) {

            for index in (0...1000) {
                self.data.append("\(index)")
            }
        }

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

        func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
            let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! GenericCell
            cell.customView.rootView = AnyView(
                Text(data[indexPath.item]).font(Font.title).border(Color.red)
            )
            return cell
        }
    }


    func makeUIView(context: Context) -> UICollectionView {
        let layout = UICollectionViewFlowLayout()
        layout.estimatedItemSize = UICollectionViewFlowLayout.automaticSize
        layout.scrollDirection = .vertical
        let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
        cv.dataSource = context.coordinator
        cv.delegate = context.coordinator
        cv.register(GenericCell.self, forCellWithReuseIdentifier: "cell")

        cv.backgroundColor = .white
        layout.minimumInteritemSpacing = 0
        layout.minimumLineSpacing = 0
        return cv
    }
    func updateUIView(_ uiView: UICollectionView, context: Context) {

    }
}


open class GenericCell: UICollectionViewCell {
    public var customView = UIHostingController(rootView: AnyView(Text("")))
    public override init(frame: CGRect) {
        super.init(frame: frame)
        configure()
    }
    public required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        configure()
    }
    private func configure() {
        contentView.addSubview(customView.view)
        customView.view.preservesSuperviewLayoutMargins = false
        customView.view.translatesAutoresizingMaskIntoConstraints = false

        NSLayoutConstraint.activate([
            customView.view.leftAnchor.constraint(equalTo: contentView.layoutMarginsGuide.leftAnchor),
            customView.view.rightAnchor.constraint(equalTo: contentView.layoutMarginsGuide.rightAnchor),
            customView.view.topAnchor.constraint(equalTo: contentView.layoutMarginsGuide.topAnchor),
            customView.view.bottomAnchor.constraint(equalTo: contentView.layoutMarginsGuide.bottomAnchor),
        ])
    }
}

Le premier écran est bon.

enter image description here

Mais en faisant défiler la fin de l'écran visible, on dirait

enter image description here

Y a-t-il quelque chose que je fais mal avec le redimensionnement automatique de mes cellules? Ou est-ce juste plus de bugs SwiftUI?

[Modifier] J'ai accepté une réponse SwiftUI, mais si quelqu'un peut me fournir un correctif pour travailler avec UIKit comme demandé dans cette question, j'accepterai .

10
swift nub

En utilisant simplement SwiftUI, vous pouvez créer une vue qui redistribue les vues de texte comme vous le souhaitez. Le code est sur GitHub , mais est assez long, donc je ne le posterai pas ici. Vous pouvez trouver mon explication de ce qui se passe dans cette réponse . Voici une démo:

enter image description here

0
John M.

J'ai fait quelques modifications et cela fonctionne mais je ne pense pas que ce soit la meilleure pratique.

import SwiftUI
import UIKit

struct ContentView: View {
    var body: some View {
        CollectionComponent()
    }
}

#if DEBUG
struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
#endif

struct CollectionComponent : UIViewRepresentable {
    func makeCoordinator() -> CollectionComponent.Coordinator {
        Coordinator(data: [])
    }

    class Coordinator: NSObject, UICollectionViewDataSource, UICollectionViewDelegate {
        var data: [String] = []

        init(data: [String]) {

            for index in (0...1000) {
                self.data.append("\(index)")
            }
        }

        func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
            data.count
        }
        func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
            return CGSize(width: collectionView.frame.width/2.5, height: collectionView.frame.width/2)
        }

        func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
            let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! GenericCell

            cell.customView?.rootView = Text(data[indexPath.item])

            return cell
        }
    }


    func makeUIView(context: Context) -> UICollectionView {
        let layout = UICollectionViewFlowLayout()
        layout.scrollDirection = .vertical
        let cvs = UICollectionView(frame: .zero, collectionViewLayout: layout)
        cvs.dataSource = context.coordinator
        cvs.delegate = context.coordinator
        cvs.register(GenericCell.self, forCellWithReuseIdentifier: "cell")

        cvs.backgroundColor = .white
        return cvs
    }
    func updateUIView(_ uiView: UICollectionView, context: Context) {

    }
}


public class GenericCell: UICollectionViewCell {

    public var textView = Text("")
    public var customView: UIHostingController<Text>?
    public override init(frame: CGRect) {
        super.init(frame: .zero)


        customView = UIHostingController(rootView: textView)
        customView!.view.translatesAutoresizingMaskIntoConstraints = false
        contentView.addSubview(customView!.view)

        customView!.view.topAnchor.constraint(equalTo: contentView.topAnchor).isActive = true
        customView!.view.leftAnchor.constraint(equalTo: contentView.leftAnchor).isActive = true
        customView!.view.rightAnchor.constraint(equalTo: contentView.rightAnchor).isActive = true
        customView!.view.bottomAnchor.constraint(equalTo: contentView.bottomAnchor).isActive = true
    }
    public required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}
0
utgoer