J'ai un ViewController avec un CollectionView et je voudrais placer le UICollectionViewDataSource dans une classe supplémentaire, afin qu'il puisse être réutilisé. (Je n'utilise pas le constructeur d'interface).
Par conséquent, j'ai créé une classe supplémentaire:
override func viewDidLoad() { super.viewDidLoad() collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "MYIDENTIFIER") let myDataSource = MyDataSource() collectionView.delegate = myDataSource collectionView.dataSource = myDataSource }
Dans le ViewController avec CollectionView, je l'ai défini:
class MyDataSource: NSObject, UICollectionViewDelegate, UICollectionViewDataSource { public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return 5 } public func numberOfSections(in collectionView: UICollectionView) -> Int { return 1 } public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "MYIDENTIFIER", for: indexPath) cell.backgroundColor = .blue return cell } }
Cependant, la collectionView restera vide - il n'y a pas de cellules dessus.
Cependant, si je mets toutes les fonctions DataSource dans la classe ViewController elle-même et définissez le délégué et DataSource sur self , alors tout fonctionne.
N'est-il pas possible d'externaliser l'UICollectionViewDataSource dans d'autres fichiers, ou avez-vous autre chose à faire?
3 Réponses :
merci pour l'aide! J'ai essayé ce que Galo a suggéré, mais pour moi, cela n'a pas fonctionné. Cependant, j'ai trouvé la solution:
Vous devez initialiser le délégué en tant que propriété du ViewController, et pas dans sa fonction viewDidLoad! Alors tout fonctionne correctement!
let myDataSource = MyDataSource() override func viewDidLoad() { super.viewDidLoad() collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "MYIDENTIFIER") collectionView.delegate = myDataSource collectionView.dataSource = myDataSource }
Juste au cas où. Voici la raison pour laquelle je disais à propos de l'appel de la méthode init. @Andrey Chernukha, désolé si cela a semé la confusion. C'est pourquoi j'ai dit que cela fonctionne avec le init
mais je suppose que c'est la même raison que Pascal
import UIKit class ViewController: UIViewController { lazy var collectionView : UICollectionView = { let layout = UICollectionViewFlowLayout() let cv = UICollectionView(frame: .zero, collectionViewLayout: layout) cv.backgroundColor = .green return cv }() var dataSource : CustomDataSource! override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view, typically from a nib. self.view.addSubview(collectionView) collectionView.frame = CGRect(x: 0, y: 0, width: self.view.frame.width, height: self.view.frame.height) dataSource = CustomDataSource(collectionView: collectionView) } } class CustomDataSource : NSObject, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout, UICollectionViewDelegate { let collectionView : UICollectionView init(collectionView: UICollectionView) { self.collectionView = collectionView super.init() self.collectionView.dataSource = self self.collectionView.delegate = self self.collectionView.register(Cell.self, forCellWithReuseIdentifier: "cell") } func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return 3 } func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) cell.backgroundColor = .red return cell } func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { return CGSize(width: 100, height: 100) } } class Cell : UICollectionViewCell { override init(frame: CGRect) { super.init(frame: frame) setupViews() } func setupViews() { self.backgroundColor = .red } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } }
Je l'ai testé et la raison est que dataSource
est faible dans une collectionView, il est donc désalloué immédiatement après son instanciation s'il est déclaré dans viewDidLoad()
C'est un autre problème de portée souvent négligé.
override func viewDidLoad() { super.viewDidLoad() collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "MYIDENTIFIER") let myDataSource = MyDataSource(). //See the problem here? collectionView.delegate = myDataSource collectionView.dataSource = myDataSource } //myDataSource will be released!
Je pense que vous devez initier le dataSource avec une collectionView. Créez un init
init (collectionView: UICollectionView)
dans votre classe dataSource@GaloTorresSevilla et comment est-il censé aider?
@AndreyChernukha, il lancera la source de données avec la collectionView. Ensuite, vous définissez le
collectionView.dataSource = self
dans la classe dataSource. Je l'ai fait de cette façon et cela a fonctionné.@GaloTorresSevilla et quelle est la différence réelle entre ce qui est fait et ce que vous suggérez? dans les deux cas, la vue de collection reçoit la source de données
Vraiment pas sûr. C'est pourquoi je dis que je pense. Je vais essayer de le découvrir