Verwirrendes CollectionViewCell/TableView Problem

Diese Seite verwendet Cookies. Durch die Nutzung unserer Seite erklären Sie sich damit einverstanden, dass wir Cookies setzen. Weitere Informationen

  • Verwirrendes CollectionViewCell/TableView Problem

    Hallo,

    ich arbeite aktuell an einem Projekt in dem sich innerhalb einer TableView in jeder Zelle eine CollectionView befindet. Ich versuche also entsprechende Daten in die jeweils richtigen Zellen zu laden jedoch passieren dabei verwirrende Dinge.
    Grundsätzlich versuche ich über meine func tableView cellForRowAt die CollectionView Zellen zu befüllen doch leider ist das erste Problem das ich schon mal habe, dass wenn ich die App starte nicht alle CollectionViewZellen von Anfang an sichtbar sind.
    Es scheint als müsste ich die TableView erst auf und ab scrollen damit die Zellen erscheinen. Die CollectionViewZellen sind erst dann sichtbar wenn sie einmal aus dem Sichtbereich verschwunden sind und dann wieder eintreten. Woran liegt das ?

    PS: Wenn ich cell.testCell.backgroundColor in der func collectionView cellForItemAt einbaue dann sind alle Zellen in blau sichtbar

    Hier die relevanten Codes:

    (TableViewController)

    Quellcode

    1. override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    2. guard let cell = tableView.dequeueReusableCell(withIdentifier: "QuestionLine", for: indexPath) as? HomeTableViewCell else { fatalError()}
    3. let userData = items.data[indexPath.row]
    4. let questionData = userData.userQuestions
    5. cell.cellLabel.text = userData.userName
    6. let imageUrl = URL(string: allProfilePictureURL + userData.profilePicture)
    7. if let url = imageUrl {
    8. cell.imageV.sd_setImage(with: url)
    9. }
    10. cell.imageV.layer.cornerRadius = cell.imageV.frame.width / 2;
    11. cell.imageV.layer.borderWidth = 1
    12. cell.imageV.layer.borderColor = UIColor.lightGray.cgColor
    13. cell.imageV.layer.masksToBounds = true
    14. cell.int = userData.userQuestions.count
    15. cell.testCell.backgroundColor = .blue
    16. tableView.tableFooterView = UIView(frame: CGRect.zero)
    17. return cell
    18. }
    19. }
    Alles anzeigen
    (TableViewCell)

    Quellcode

    1. import UIKit
    2. import SDWebImage
    3. class HomeTableViewCell: UITableViewCell {
    4. @IBOutlet var cellLabel: UILabel!
    5. @IBOutlet var imageV: UIImageView!
    6. var int = Int()
    7. var testCell = UICollectionViewCell()
    8. }
    9. extension HomeTableViewCell: UICollectionViewDataSource, UICollectionViewDelegate {
    10. func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
    11. return int
    12. }
    13. func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    14. testCell = collectionView.dequeueReusableCell(withReuseIdentifier: "HomePhotoCell", for: indexPath)
    15. return testCell
    16. }
    17. }
    18. extension HomeTableViewCell: UICollectionViewDelegateFlowLayout {
    19. func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
    20. let itemsPerRow:CGFloat = 2
    21. let hardCodedPadding:CGFloat = 5
    22. let itemWidth = (collectionView.bounds.width / itemsPerRow) - hardCodedPadding
    23. let itemHeight = collectionView.bounds.height - (2 * hardCodedPadding)
    24. return CGSize(width: itemWidth, height: itemHeight)
    25. }
    26. }
    Alles anzeigen

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von ThisIsBeat ()

  • Die Daten für die TableViewCell sind schon sehr verwirrend. Es gibt einmal userData, welche aus items.data für die aktuelle Zeile ermittelt werden (let userData = items.data[indexPath.row]).

    An die TableViewCell übergibst Du dann aber wieder alle Daten (cell.userDataForCell = items.data) und nicht nur die Daten für die aktuelle Zeile. Warum? ?(
  • Ups sorry mein Fehler, das war noch ein Überbleibsel von etwas das ich ausprobiert habe. Habe das ganze jetzt schon raus genommen :)

    Wobei wie genau meinst du das mit den Daten für die aktuelle Zeile ? Wie soll ich denn die Daten für items.data[indexPath.row] als gesamtes übergeben ?

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von ThisIsBeat ()

  • Ich tippe mal, das der UICollectionView bereits collectionView(_:numberOfItemsInSection:) aufgerufen hat, bevor Du die Anzahl der userQuestions in der Zelle gesetzt hast.

    Du solltest daher einfach nach dem Setzen der Anzahl der userQuestions ein reloadData() bei dem UICollectionView ausführen.
  • Also das ganze liegt natürlich nahe doch leider hat ein reloadData() nichts geändert.
    Es sieht auch ehrlich gesagt so aus als wenn die CollectionView an sich Inhalt hat weil ich darin scrollen kann. Nur die Zellen sind halt nicht sichtbar ....

    In welcher Funktion hättest du jetzt genau den reloadData() aufgerufen ?
    numberOfRowsInSection und cellForRowAt hat nicht geholfen :/

    Ich verstehe das ganze einfach nicht. Das hier funktioniert z.B ist aber nicht mein Ziel. Wieso will das denn nicht über die TableView funktionieren...

    Quellcode

    1. func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    2. testCell = collectionView.dequeueReusableCell(withReuseIdentifier: "HomePhotoCell", for: indexPath)
    3. testCell.backgroundColor = .blue
    4. return testCell
    5. }
  • Ich collectionView(_:cellForItemAt:) erzeugst bzw. reused Du zwar eine UICollectionViewCell, gibst dieser allerdings keinen Inhalt. Macht ja wenig Sinn, leere CollectionViewCells anzuzeigen, oder?

    ThisIsBeat schrieb:

    In welcher Funktion hättest du jetzt genau den reloadData() aufgerufen ?

    Nach dem Setzen der Anzahl der userData.userQuestions in tableView(_:cellForRowAt:). In Objective-C würde man z.B. den Setter von int (sehr schlechter Name übrigens. Ich musste mehrfach den Code lesen, um diesen zu verstehen) überschreiben und darin ein reloadData() bei dem UICollectionView aufrufen.

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von MCDan ()

  • Okay also reloadData() auf die CollectionView anzuwenden scheint überhaupt keine Auswirkungen zu haben egal wo ich das ganze aufrufe....
    Hatte dann noch in der cellForRowAt so etwas probiert und tatsächlich sind mir dabei dann einzelne Zellen angezeigt wurden, jedoch nicht alle:

    Quellcode

    1. DispatchQueue.main.async {
    2. cell.testCell.backgroundColor = .red
    3. }




    Aber nun gut das ganze scheint aus irgendwelchen Gründen auch immer nicht zu funktionieren. Vielleicht gibt es ja einen Weg der um das Problem herum führt und zwar ist Sinn und Zweck der ganzen Geschichte das ich pro TableViewCell, User Informationen in die einzelnen CollectionViewCells lade...

    Das hier war einer meiner Tests in der auch die von dir angemerkte Variable userDataForCell eine Rolle spielt:

    Quellcode

    1. func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    2. testCell = testCollectionView.dequeueReusableCell(withReuseIdentifier: "HomePhotoCell", for: indexPath)
    3. let userData = userDataForCell[indexPath.row]
    4. let questionData = userData.userQuestions[indexPath.row]
    5. let questionImageView: UIImageView = UIImageView(image: nil)
    6. let imageToDisplay: URL? = URL(string: allQuestionPictureURL + questionData.questionImages[0])
    7. if let imageUrl = imageToDisplay {
    8. questionImageView.sd_setImage(with: imageUrl)
    9. }
    10. questionImageView.frame = CGRect(x: 0, y: 0, width: testCell.frame.size.width, height: testCell.frame.size.height)
    11. questionImageView.contentMode = .scaleAspectFill
    12. testCell.addSubview(questionImageView)
    13. return testCell
    14. }
    15. }
    Alles anzeigen
    Mir werden dabei auch die Zellen angezeigt und darin sind auch Bilder enthalten. Nur leider sind es pro Section immer die selben Bilder. Mir ist klar dass das an userData und questionData liegt, weil ich der Zelle ja auch sage so zu arbeiten.
    Das ganze sieht so aus weil ich nicht weiß wie ich es anders machen kann.
    Letztendlich weiß meine TableView ja in welche Section welche Daten gehören. Jetzt die Frage, gibt es einen Weg wie ich ausgehend von der TableView cellForRowAt die richtigen Daten an meine CollectionViewCell übertragen kann ?

    Hier auch nochmal die cellForRowAt Funktion:

    Quellcode

    1. override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    2. guard let cell = tableView.dequeueReusableCell(withIdentifier: "QuestionLine", for: indexPath) as? HomeTableViewCell else { fatalError()}
    3. let userData = items.data[indexPath.row]
    4. let questionData = userData.userQuestions[indexPath.row]
    5. cell.cellLabel.text = userData.userName
    6. let imageUrl = URL(string: allProfilePictureURL + userData.profilePicture)
    7. if let url = imageUrl {
    8. cell.imageV.sd_setImage(with: url)
    9. }
    10. cell.imageV.layer.cornerRadius = cell.imageV.frame.width / 2;
    11. cell.imageV.layer.borderWidth = 1
    12. cell.imageV.layer.borderColor = UIColor.lightGray.cgColor
    13. cell.imageV.layer.masksToBounds = true
    14. cell.int = userData.userQuestions.count
    15. cell.userDataForCell = items.data
    16. tableView.tableFooterView = UIView(frame: CGRect.zero)
    17. return cell
    18. }
    Alles anzeigen

    Vielleicht ist dabei auch noch das JSON Paket ganz interessant fürs Verständnis:

    Quellcode

    1. "status":"ok",
    2. "data":[
    3. {
    4. "user_id":"4w6fxth",
    5. "user_username":"MaxMusti",
    6. "user_profile_picture":"3415330915623892.png",
    7. "user_questions":[
    8. {
    9. "question_title":"Valence ist cool",
    10. "question_creation":"1530094877",
    11. "question_expires":"1847998454",
    12. "question_id":"t4a6adfgGRDa",
    13. "question_images":[
    14. "Taebui9N.jpg",
    15. "saeSh6ip.jpg",
    16. "reec2Bai.jpeg",
    17. "Ohbii3mu.jpg"
    18. ]
    19. }
    20. ]
    21. },
    22. {
    23. "user_id":"gfsztrs65",
    24. "user_username":"JoELCHAPO",
    25. "user_profile_picture":"3215330915627845.jpg",
    26. "user_questions":[
    27. {
    28. "question_title":"Was geht",
    29. "question_creation":"1530094734",
    30. "question_expires":"1947998453",
    31. "question_id":"TtH6f5",
    32. "question_images":[
    33. "ohreiTe5.png",
    34. "Ooroo7oo.png",
    35. "Oodei3pu.jpg"
    36. ]
    37. },
    38. {
    39. "question_title":"Test",
    40. "question_creation":"1530094735",
    41. "question_expires":"1947998454",
    42. "question_id":"A56BN4fgr",
    43. "question_images":[
    44. "ohreiTe5.png",
    45. "Ooroo7oo.png"
    46. ]
    47. }
    48. ]
    49. },
    50. {
    51. "user_id":"dkugcii",
    52. "user_username":"philpru98",
    53. "user_profile_picture":"3515330915623957.jpeg",
    54. "user_questions":[
    55. {
    56. "question_title":"Coole Schuhe",
    57. "question_creation":"1530094887",
    58. "question_expires":"1947998455",
    59. "question_id":"t4ezsyxzxzZ",
    60. "question_images":[
    61. "Taebui9N.jpg",
    62. "saeSh6ip.jpg",
    63. "reec2Bai.jpeg",
    64. "ohreiTe5.png",
    65. "thae0Eej.png"
    66. ]
    67. },
    68. {
    69. "question_title":"Test3",
    70. "question_creation":"1530094887",
    71. "question_expires":"1947998456",
    72. "question_id":"fdsa94haul",
    73. "question_images":[
    74. "Ooroo7oo.png",
    75. "up6pho4A.jpeg"
    76. ]
    77. },
    78. {
    79. "question_title":"Test4",
    80. "question_creation":"1530094887",
    81. "question_expires":"1947998464",
    82. "question_id":"789kdaRPu9a",
    83. "question_images":[
    84. "ohreiTe5.png",
    85. "iePh5che.jpg"
    86. ]
    87. }
    88. ]
    89. }
    90. ],
    91. "time":1550345839
    92. }
    Alles anzeigen
  • Die Frage ist jetzt erst mal, was Du genau wo/wie anzeigen möchtest? ;)

    Weiterhin solltest Du Ratschläge/Lösungen aus anderen Posts berücksichtigen und nicht die in den anderen Posts aufgetretenen Probleme/Fehler immer wieder neu verwenden. Diese Probleme/Fehler lassen sich nur lösen, wenn Du die Ratschläge/Lösungen auch verwendest. ;)
  • Ja du hast recht, manchmal halte ich an altem Code fest weil ich mir nicht anders zu helfen weiß. Aber gut dann zäunen wir das Pferd mal von hinten auf...

    Also ich habe ja ein TableView bei der sich in jeder Zelle eine CollectionView befindet. Jeder User bekommt seine eigene TabeViewCell.
    In meinem JSON Paket befindet sich die Variable user_questions. Je nachdem wie viele Fragen ein User also gestellt hat, werden diese da drin gesammelt.

    Ich möchte jetzt das pro Frage in user_questions immer das erste Bild von question_images in einer CollectionViewCell zu sehen ist.



    Mein Problem liegt also da drin den CollectionViewCells den richtigen Inhalt zu vermitteln. Dabei also die Frage die ich mir stelle: Von wo aus sollte ich die CollectionViewCells befüllen. tableView cellForRowAt oder collectionView cellForItemAt ?
  • ThisIsBeat schrieb:

    Mein Problem liegt also da drin den CollectionViewCells den richtigen Inhalt zu vermitteln. Dabei also die Frage die ich mir stelle: Von wo aus sollte ich die CollectionViewCells befüllen. tableView cellForRowAt oder collectionView cellForItemAt ?
    Natürlich in collectionView:cellForItemAtIndexPath:

    Der TableViewCell übergibst Du dann das komplette userData.userQuestions Array mit den Fragen. Diese Daten sind dann die Grundlage für den CollectionView.
  • Oh man man man. Manchmal kann es so einfach sein. Ich weiß auch nicht, ich hatte die ganze Zeit über einen riesigen Denkfehler. An der Stelle vielen vielen Dank für deine Hilfe, ich glaube ich wäre ohne dich nicht darauf gekommen.

    Also hier jetzt der Code:

    Quellcode

    1. func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    2. testCell = testCollectionView.dequeueReusableCell(withReuseIdentifier: "HomePhotoCell", for: indexPath)
    3. let cell = questionForCell[indexPath.row]
    4. let questionImageView: UIImageView = UIImageView(image: nil)
    5. let imageToDisplay: URL? = URL(string: allQuestionPictureURL + cell.questionImages[0])
    6. if let imageUrl = imageToDisplay {
    7. questionImageView.sd_setImage(with: imageUrl)
    8. }
    9. questionImageView.frame = CGRect(x: 0, y: 0, width: testCell.frame.size.width, height: testCell.frame.size.height)
    10. questionImageView.contentMode = .scaleAspectFill
    11. testCell.addSubview(questionImageView)
    12. return testCell
    13. }
    14. }
    Alles anzeigen

    Und in der tableView cellForRowAt:

    cell.questionForCell = questionData

    Ich hoffe mal das jetzt von hier aus alles klappt. Sieht zumindest gut aus, ansonsten melde ich mich nochmal !
  • Ich kriege übrigens immer in der Konsole folgendes ausgespuckt:

    HomeScreenDemo[14192:662472] The behavior of the UICollectionViewFlowLayout is not defined because:
    2019-02-19 19:27:44.590006-0700 HomeScreenDemo[14192:662472] the item height must be less than the height of the UICollectionView minus the section insets top and bottom values, minus the content insets top and bottom values.

    2019-02-19 19:27:44.590891-0700 HomeScreenDemo[14192:662472] The relevant UICollectionViewFlowLayout instance is <UICollectionViewFlowLayout: 0x7fabecd37f60>, and it is attached to <UICollectionView: 0x7fabed898000; frame = (0 59; 375 276); clipsToBounds = YES; autoresize = RM+BM; gestureRecognizers = <NSArray: 0x600001f206f0>; layer = <CALayer: 0x600001113540>; contentOffset: {0, 0}; contentSize: {40, 276}; adjustedContentInset: {0, 0, 0, 0}> collection view layout: <UICollectionViewFlowLayout: 0x7fabecd37f60>.
    2019-02-19 19:27:44.591039-0700 HomeScreenDemo[14192:662472] Make a symbolic breakpoint at UICollectionViewFlowLayoutBreakForInvalidSizes to catch this in the debugger.

    Ich habe herausgefunden das es an der height in folgender Funktion liegt:

    Quellcode

    1. extension HomeTableViewCell: UICollectionViewDelegateFlowLayout {
    2. func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
    3. let itemsPerRow:CGFloat = 2
    4. let hardCodedPadding:CGFloat = 5
    5. let itemWidth = (collectionView.bounds.width / itemsPerRow) + (2 * hardCodedPadding)
    6. let itemHeight = collectionView.bounds.height - (2 * hardCodedPadding)
    7. return CGSize(width: itemWidth, height: itemHeight)
    8. }
    9. }
    Jetzt ist aber die Sache das mir die Höhe der Zellen so gefällt. Was kann ich also machen um die Konsolenausgabe zu verhindern ?
  • ThisIsBeat schrieb:

    Du meinst sicher viewWithTag() ? Aber ist es in dem Fall nicht sogar gut wenn immer eine neue ImageView hinzugefügt wird wenn sie gebraucht wird ?

    Ich sehe keine Sinn darin, einen vorhandenen ImageView nicht erneut zu verwenden. Wenn es dann doch ein neuer ImageView sein muss, dann solltest Du den alten ImageView wenigstens entfernen. Wenn eine CollectionViewCell Zelle z.B. 20 x Reused wird, dann hast Du aktuell 19 überfüssige ImageViews in dieser CollectionViewCell. Da könnte es irgendwann Speicher und Performance Probleme geben, da ja auch 19 überfüssige Images im Speicher gehalten werden. ;)
  • also ich denke jetzt an eine Funktion wie imageView.removeFromSuperview(). Ich weiß nicht ob das sinnvoll wäre aber vielleicht so ?

    Quellcode

    1. override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    2. guard let cell = tableView.dequeueReusableCell(withIdentifier: "QuestionLine", for: indexPath) as? HomeTableViewCell else { fatalError()}
    3. homeTableView.rowHeight = 335
    4. let userData = items.data[indexPath.row]
    5. let questionData = userData.userQuestions
    6. cell.cellLabel.text = userData.userName
    7. let imageUrl = URL(string: allProfilePictureURL + userData.profilePicture)
    8. if let url = imageUrl {
    9. cell.imageV.sd_setImage(with: url)
    10. }
    11. cell.imageV.layer.cornerRadius = cell.imageV.frame.width / 2;
    12. cell.imageV.layer.borderWidth = 1
    13. cell.imageV.layer.borderColor = UIColor.lightGray.cgColor
    14. cell.imageV.layer.masksToBounds = true
    15. cell.numberOfCells = userData.userQuestions.count
    16. cell.questionForCell = questionData
    17. //NEU
    18. cell.questionImageView.removeFromSuperview()
    19. cell.homeCollectionView.reloadData()
    20. return cell
    21. }
    Alles anzeigen
  • Es ging mir um die ImageViews in den CollectionViewCells, welche Du per Code erzeugst und zur CollectionViewCell hinzufügst. Siehe Post 11.

    Bei dem Code wird bei jedem Reuse der CollectionViewCell ein neuer ImageView erzeugt und zur CollectionViewCell hinzugefügt. Dort solltest Du einen ggf. vorhandenen ImageView wiederverwenden, oder einen bereits vorhanden ImageView per removeFromSuperview() entfernen, wenn Du unbedingt einen neuen ImageView erzeugen möchtest.
  • Also gut ich muss sagen das ich sowas noch nie gemacht habe mir aber die Problematik bewusst ist. Also wenn ich dich richtig verstehe schlägst du also vor erstmal zu prüfen ob die ImageView überhaupt existiert oder ?

    Ich habe das hier jetzt über contains() gemacht um einfach mal zu sehen was passiert, aber es wird immer "nicht vorhanden" geprintet...

    Quellcode

    1. func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    2. testCell = testCollectionView.dequeueReusableCell(withReuseIdentifier: "HomePhotoCell", for: indexPath)
    3. let cell = questionForCell[indexPath.row]
    4. let questionImageView: UIImageView = UIImageView(image: nil)
    5. //NEU
    6. if cell.subviews.contains(questionImageView) {
    7. print("vorhanden")
    8. } else {
    9. print("nicht vorhanden")
    10. }
    11. let imageToDisplay: URL? = URL(string: allQuestionPictureURL + cell.questionImages[0])
    12. if let imageUrl = imageToDisplay {
    13. questionImageView.sd_setImage(with: imageUrl)
    14. }
    15. questionImageView.frame = CGRect(x: 0, y: 0, width: testCell.frame.size.width, height: testCell.frame.size.height)
    16. questionImageView.contentMode = .scaleAspectFill
    17. testCell.addSubview(questionImageView)
    18. return testCell
    19. }
    20. }
    Alles anzeigen