Problème dans UITableViewDelegate – RowSelected donne un mauvais NSIndexPath

J'ai un UITableViewSource que j'ai sous-classé. Je substitue GetCell et utilise mes propres cellules sous-classées, comme ceci:

public override UITableViewCell GetCell(UITableView tableView, NSIndexPath indexPath) { MarketItem item=_tableItems[indexPath.Section].Items[indexPath.Row]; MarketCell cell=tableView.DequeueReusableCell(_cellIdentifier) as MarketCell; if (cell==null) { cell=new MarketCell(UITableViewCellStyle.Subtitle,_cellIdentifier,item); } // decorate the cell // ... return cell; } 

Cela fonctionne mais quand j'obtiens des events dans mon UITableViewDelegate, le path d'index m'obtient la mauvaise cellule (events comme AccessoryButtonTapped, WillSelectRow etc.).

Les numéros de section et de ligne semblent corrects mais quand je fais un

  tableView.CellAt(indexPath) 

Je reçois la mauvaise cellule. (Les numéros de ligne et de section semblent à nouveau corrects.)

Choses à noter:

  • La table est constamment mise à jour – les éléments arrivent dans un thread différent qui sont ensuite InvokeOnMainThread'd
  • Bien que le tableau soit constamment mis à jour, les lignes et les sections sont seulement ajoutées – rien n'est réordonné ou supprimé
  • Si je mets en pause les mises à jour quand j'obtiens un 'WillSelectRow', ça n'aide pas
  • Le plus intéressant (mais pas une solution livrable) si je fais une nouvelle cellule chaque fois plutôt que de faire DequeueReusableCell, cela fonctionne correctement.

Je ne peux pas m'empêcher de penser que c'est un bug stupide de ma propre fabrication, mais je ne le trouve pas. Toute aide serait très appréciée!

Vous pourriez essayer une approche différente comme démontré sur: http://simon.nureality.ca/?p=91

Fondamentalement, ne sous-classz pas UITableViewCell mais sous-class UIViewController comme "MarketCellController". Ce controller personnalisé maintient un UITableViewCell standard ainsi que vos trucs personnalisés et ajoute simplement vos trucs personnalisés via AddSubview ().

Vous créez un nouveau controller pour chaque cellule requirejse et les stockez dans un dictionary.

L'astuce: En atsortingbuant des tags uniques aux cellules, vous pouvez récupérer le controller associé dans le dictionary.

Exemple rapide:

 Dictionary<int,MarketCellController> controllers = new Dictionary<int,MarketCellController>(); // ... public override UITableViewCell GetCell(UITableView tableView, NSIndexPath indexPath) { UITableViewCell cell = tableView.DequeueReusableCell(_cellIdentifier); MarketCellController cellController; if (cell == null) { cellController = new MarketCellController(); cell = cellController.Cell; controllers.Add(cell.Tag, cellController); } else { cellController = controllers[cell.Tag]; } // Decorate the cell (using Methods of your cellController) // ... return cell; } 

Cela pourrait éventuellement contourner les problèmes avec UITableViewCells sous-classé et dequeuing.

MODIFIER:

J'avais juste quelque chose d'autre à l'esprit: vous semblez n'assimiler que vos articles sur votre constructor MarketCell. Toutefois, les cellules supprimées ont leur ancien jeu d'éléments et vous devez le réinitialiser sur le nouvel élément après la suppression de la queue.

Supprimez le paramètre item du constructor (il sera de toute façon écrasé) et créez une propriété publique (ou setter) à la place. Utilisez-le pour affecter l'élément correct après avoir récupéré la cellule (qu'elle ait été supprimée ou nouvellement créée). Cela devrait faire:

 MarketCell cell = tableView.DequeueReusableCell(_cellIdentifier) as MarketCell; if (cell == null) { cell = new MarketCell(UITableViewCellStyle.Subtitle, _cellIdentifier); } // Assign the correct item cell.Item = item; // or (whatever you like more): // cell.SetItem(item); // Decorate the cell // ... return cell; 

Il en va de même pour mon approche de CellController. Peu importe si une cellule a été retirée ou nouvelle, vous devez toujours tout réinitialiser sur la cellule qui pourrait éventuellement différer de toutes les autres cellules.

BTW: Si vous codez vos cellules à UITableViewCellStyle.Subtitle, vous pouvez simplement l'omettre du constructor et le coder en dur dans votre class MarketCell.

En ce qui concerne la valeur de l'approche cellController: vous découplez UITableViewCell de vos données et comportements personnalisés et obtenez ainsi une couche de séparation agréable. Votre controller de cellule n'a pas à agir comme une cellule de table;)

Il semble que ce soit encore un problème dans Xamarin. Le lien posté par @ vlad259 a malheureusement disparu depuis longtime – et ce 'bug' vient de me mordre aujourd'hui. Je ne sais pas pourquoi cela se produit – dans l'intérêt de la search de la cause, j'ai recouru à la réduction de mon implémentation UITableViewSource pour renvoyer un nombre fixe de lignes et simplement assigner le numéro de ligne à l'label de la cellule. ..

Le bug se manifeste spécifiquement comme, chargez la vue, faites défiler jusqu'à la première cellule hors écran (si votre table peut rendre 10 cellules, faites défiler la 11e juste à l'écran), puis appuyez sur cette cellule – vous obtiendrez une Index de ligne dans votre événement RowSelected.

J'ai trouvé quelques solutions à cela.

Solution A

Ce bogue ne semble se manifester que si j'ai remplacé GetHeightForRow pour returnner des hauteurs de ligne personnalisées – même s'il returnne une constante. Si ce n'est pas nécessaire … ne le remplacez pas.

Solution B

Il semble que le bug est spécifiquement dans le DequeueReusableCell (s) qui prend un argument NSIndexPath .

Utilisation

UITableView.DequeueReusableCell(NSSsortingng)

au lieu de

UITableView.DequeueReusableCell(NSSsortingng, NSIndexPath)