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.
Mais en faisant défiler la fin de l'écran visible, on dirait
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 .
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:
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")
}
}