création et remplissage tableView (code) sans storyBoard
toolsDev
Membre
Bonjour,
j'aimerais savoir comment implémenter une tableView (code), sans utilisé le storyBoard et dans un viewController ?
Le début est correcte mais c'est pour l'ajout des données que je sèche :
let tableView:UITableView = UITableView(frame: CGRect(x:130, y: 30, width:200, height:400))
tableView.reloadData()
tableView.backgroundColor = UIColor(red: 85/255, green: 175/255, blue: 101/255, alpha: 1.0)
// compte le nombre d'éléments de mon tableau
tableView.numberOfRows(inSection:connexion.arrayData.count)
/ puis ma boucle pour ajouter mes données :
for data in connexion.arrayData
{
let cell = tableView.dequeueReusableCell( withIdentifier:??, for:??)
cell.myLabel.text = data
}
self.view.addSubview(tableView)
// avec ce code ma tableView s'affiche mais je ne sais pas comment remplir les champs "??"
En fait, j'aimerais pouvoir "mapper" automatiquement les colonnes et les lignes de ma requête en base, sur ma tableView.
Car mon tableau "data" contient les champs de mes tables, exemple :
id:1
name: nom
description: description
etc
Sans forcément les mêmes champs
En somme remplir un tableau automatiquement en respectant colonnes/lignes... (mais avec la possibilité de ne pas afficher certaines lignes (exemple les id)
Merci à toutes et tous
Connectez-vous ou Inscrivez-vous pour répondre.
Réponses
Mais pourquoi ? Tu veux aussi poser et gonfler les pneus sur les roues avant chaque fois que tu conduises ta bagnole ?
Le début n'est pas du tout correct.
1. Tu n'as pas considéré les contraintes pour maintenir la position à la rotation
2. Le seul moyen de remplir une tableView, c'est par son delegate et son dataSource
2. tu appelles reloadData avent que tu as connecté la tableView à son delegate et son dataSource
3. tu n'as pas implémenté ni le delegate ni le dataSource
4. les méthodes que tu as appelé sur la tableView sont les méthodes que l'on ne doive pas appeler, elles devraient être appelé seulement par le tableView lorsque elle en a besoin.
Les tableViews ne comprennent pas les colonnes. Il faut considérer construire les cellules avec multiples parties ou d'utiliser une UICollectionView.
Mais je répète - pourquoi ne pas utiliser un storyboard ?
si je voulais faire ainsi, c'est pour ne pas avoir ma tableView sur ma vue.
donc si je la place sur le storyboard je vais avoir un empilement d'objets, voilà tout...
la tableView doit être positionné à un certain endroit au clique d'un bouton. Alors quoi de plus pratique que je la construire à la volé ! plutôt que de la mettre déjà en place.
Mais vu ta réponse je pensais encore mal
Ton approche est logique, et valable dans d'autres contextes, mais ce n'est pas de cette manière que fonctionnent les table view sous iOS.
Pour des questions d'efficacité mémoire et de vitesse d'exécution, ce n'est pas toi qui mets des cellules dans la tableview, c'est elle qui te demande ce qu'elle contient quand elle a besoin de le savoir.
Pour répondre à ses besoins, tu implémentes le protocol UITableViewDataSource.
Généralement, on implémente ce protocol dans le view controller qui gère la vue dans laquelle tu as mis ta table view.
Si ta vue ne contient que la table view, tu peux utiliser la classe UITableViewController comme controller.
Au minimum ton objet qui implémente le protocol datasource doit répondre à ces deux questions :
La première méthode est appelée dès que la table view doit s'afficher. tu dois renvoyer le nombre total de cellules dans ta vue (tu verras les sections plus tard).
La deuxième méthode te demande de renvoyer la cellule à afficher pour une certaine ligne.
Exemple de ce qu'il faut faire au minimum :
Ici on suppose que la tableview est déjà créée mais tu peux la créer dans la méthode viewDidLoad.
Depuis qu'on utilise Autolayout, il plus pratique de créer et de positionner les vues dans un storyboard.
Mais tu peux toujours le faire dans le code en créant toutes les contraintes Autolayout à la main. quand je le fais, j'utilise les macros d'Erica Sadun (mais il y a surement plus récent).
Petite précision sur dequeueReusableCell :
En plus du principe du datasource, il ya le fait que les cellules peuvent être recyclées. Ce n'est pas obligatoire. Par exemple si tu n'as qu'une poignée de cellules à afficher tu peux te passer du recyclage, comme dans l'exemple ci-dessus.
dequeueReusableCell est utilisé pour obtenir une nouvelle cellule (elle sera créée ou issue d'un recyclage).
L'identifiant correspond au type de cellule que tu veux créer (tu peux faire cohabiter des cellules qui ont des formats différents).
Tu dois utiliser register(_:forCellReuseIdentifier) pour faire correspondre ton identifiant à ton type de cellule avant l'affichage, par exemple dans ViewdidLoad.
Il y a des centaines de tuto sur le sujet. Il faut juste en trouver un le plus récent possible.
Quelques tutos français en vidéo sur le sujet : http://pagesperso.lip6.fr/Fabrice.Kordon/5I452-2014/semaine-08.php
En fait, un tableau à plusieurs entrées serait parfait ! (comme ce qu'on à en java JTable...)
My 2 cents :
Essaye d'abord de faire avec une UITableView dans un Storyboard/xib.
Ensuite, passe ensuite ta UITableView (au niveau du rendering) uniquement par code.
Pourquoi ?
J'ai l'impression que tu as d'abord un soucis avec ton modèle (construction et implémentation du dataSource), et du coup, tu te rajoutes une complication en plus en voulant faire l'UI aussi uniquement par code.
Si tu veux changer ce que la tableView montre, il suffit de changer la liste que tu utilises pour fournir les données. Ce n'est pas nécessaire de changer la tableView. C'est les méthodes du delegate qui fournissent les données.
Et il faut cinq méthodes pour les boutons, c'est bien plus simple que devoir récupérer les "senders".
Du coup, tu aurais qqch comme :
UITableView ne marche pas du tout comme JTable.
JTable est rempli avant de l'utiliser.
UITableView est rempli dynamiquement des méthodes du dataSource et on peut changer les données à n'importe quel moment.
Pour aller doucement et bien comprendre, j'ai un gros soucis de compréhension sur les types :
j'ai mes données (provenant de ma requête/méthode) sous forme d'un tableau var arrayDataTable:[Any] = [] :
et au niveau de
Donc, je sais que cell.textLabel? = "attends" un type String, mais je lui affecte un type Any...
et même en le parsant (as! Any) j'ai cette erreur de compilation.
voilà ,
a
Tu sais que Xcode attend un String, alors tu cherches à convertir un [Any] en un Any ???
Pourquoi ne pas convertir le [Any] en un .. String ?
d
J'ai l'impression que tu maitrises mal les conversions de type en Swift.
a
Dans l'exemple de code donné par FKDEV, le tableau data est de type [String]. C'est le type de base quand on tape quelque chose comme :
a
qa
Ce qui lui permet de lire directement le tableau pour obtenir un String
a
a
Pour être dans les mêmes conditions que toi, j'ai créé un tableau de [Any], en précisant sa nature:
a
Pour avoir un String je dois lire un Any dans le tableau et le convertir :
a
A lire le message d'erreur :
a
a
j'ai l'impression que ton tableau de [Any] n'est PAS un tableau, mais juste un Any contenant plein de trucs dedans.
Je ne sais pas trop ce que tu récupères avec ta connexionSQLite (je ne comprend rien du tout à SQL), mais ce n'est certainement pas un tableau de String bien structuré, utilisable par Swift.
EDIT : J'ai relu le début du post. Ton problème est bien plus compliqué que de lire le contenu d'un tableau. Tu as un flux de données contenant différentes informations. Il faut analyser ce flux pour en extraire les informations et les stocker quelque part avant de les utiliser. C'est un problème distinct de l'utilisation d'un UITableView.
J'arrive pourtant très bien à récupérer le bon index et à le passé en paramètre dans ma méthode qui "requête" la base en fonction du choix de l'utilisateur.
Mon réelle soucis est comme expliquer plus bas, est bien le type ("Row") que je récupère directement depuis ma requête via GRDB.swift Impossible de itéré ce "faux" tableau...
alors comme tu as put le lire dans le quote du code, j'ai cité le soucis.
c'est juste une erreur de ma part j'ai bien sur tenté un parse en String mais sans succès.
Et non pas si simple que ça malheureusement (enfin pour moi)
J'ai un tableau Brut sans clé/valeur, c'est bien GRDB.swift qui me mène la vie dur et non ma méthode.
Dans le sens, ou je ne peut rien parser ou pire parcourir...
Tout à fait
J'allais vous le redire hier... Mon soucis est que je n'ai pas la main sur GRBD.swift et il me renvois un tableau dont je ne peut rien parsé (à ma connaissance, sans le traité AVANT...)
J'aurais bien aimer que ma requête renvois un dictionary: clé/valeur.
du coup je me retrouve avec un tableau brute ou comme tu le signales un flux, inutilisable, finalement BEAUCOUP d'énergie pour pas grand chose encore...
Pourquoi cela n'est il pas aussi simple ?... une requête dans un tableau, je parcours ce tableau et basta...
Si je dois ENCORE changer de framework... ou si vous avez des idées sur une façon simple de récupérer directement ma requête en dictionary ou array je suis preneur ?
Edit :
je réitère ma demande (de mon autre poste)
VOUS qu'est ce que vous utilisez pour avoir la main sur vos requêtes SQLite et en même temps de "simple" ?
Je ne comprends vraiment pas ce système usine à gaz
Je ne connait rien à SQL, encore moins à SRBD.swift, mais en faisant une recherche simple sur Google, on trouve de la documentation et des exemples d'utilisation :
http://cocoadocs.org/docsets/GRDB.swift/0.4.0/
Tu devrais trouver ton bonheur dedans, non ?
merci pour ton geste, vraiment
mais, avant de posté je cherche toujours (de mon mieux) et pour ça aucun soucis.
J'ai trouvé comment "requêter" Mais, le soucis est bien ce que me ramène GRDB.swift... C'est à un tableau(brut) que je ne sais pas parcourir visiblement...
je te met mon code que tu vois (il n'y à rien de compliquer) :
pas de clé/valeurs ! et ça, c'est pénible...
je n'ai aucun soucis coté SQL, je me débrouille bien de ce coté (en même temps je fais du très simple... la seule requêtes "compliquer" est celle pour récupérer dynamiquement toutes les tables de ma base...)
Non, mon vrai soucis est de traiter l'info !
C'est vraiment pénible pur pas grand chose (enfin de mon point vue)
Je me doute que vous, vous aillez l'habitude, mais franchement SQL et ios ... c'est pas simple pour rien du tout...
Edit:
je rajouterais que je souhaite tout récupérer au lancement de l'application, pour tout mettre dans un tableau et du coup, ne plus faire de requêtes.
Cela rest bien mieux niveau performances, de mon point vue. Une requête, un fois, une affectation dans un tableau/dico... puis des itérations clé/valeur.
Si j'en crois leur Read.me
J'ai l'impression que toi tu fais un truc bizarre an ayant:
Comme l'avait signalé la nounourse Joanna, il faut que tu aies une classe qui représente ton object correctement, représentant ce que faut ton object fetché.
En Objective-C, j'aurais :
Lors du fetch, je ferais alors quelque chose du genre (pseudo code) :
Or, j'ai l'impression que toi tu fais quelque chose du genre:
Pour faire du pseudo code Swift:
Et, en Swift, tu aurais :
ce que tu m'indiques est fait :
mais pour les champs je les veut tous... du coup je suis bloquer avec un tableau qui agglomère clé/valeur, et je ne peut pas les distingués.
Mais quand je tente la même chose pour les données (lignes)
Bon, je vais réfléchir à tout ça, j'avoue je suis paumé là ! soucis de conception, peut être, mais GRDB.swift est pas très lisible pour moi.
Mappé ? oui bien sur mais je ne souhaite pas une classe "Jeux" (par exemple) statique... je veux du dynamique, tu vois ?
si ma table à des ajout de colonne, car si je fait un ORM je dois lui renseigner a la mano toutes les colonnes...
Voilà ma vision.
Tant que tu n'aurais pas réussi à le faire/comprendre en "statique" comme tu dis, tu auras du mal à le faire en "dynamique".
Et tu auras tout de même un soucis à l'affichage, dans tous les cas.
Tu te rajoutes une dose de complexité, qui te ralentis énormément. Tu as besoin d'abord de maà®triser des concepts.
Donc, il te faudra utiliser un dictionnaire de [String : Any] mais, là , tu auras les mêmes soucis de mauvais types.
Il faut accepter que tu n'est plus dans le pays de Java ; On parle ici de Swift, qui est un langage dont les types sont strictement appliqués.
Si tu veux le dynamisme, il faut chercher ailleurs
je comprends et j'entends ce que tu me dis.
à vouloir faire simple et dynamique, ça devient une usine à gaz
les concepts de type optional() je commence à m'y faire, et le reste aussi, mais je ne me sent pas si naze que ça, mais ! oui j'ai des lacunes...
mon principale soucis, je le redis... c'est la maitrise de ce framework et de géré mes tableaux... niveau objets/méthode etc.
je trouve que je m'en sort pas trop mal non ?
Avec Swift, tu as la choix - simple ou dynamique 8--)
Les optionals sont là pour te protéger contre les pièges de nils inattendus. C'est assez facile en concept mais il faut apprendre comment ils se comportent et, si tu te trouves a avec les ! partout, il faut repenser. Les if let et guard let sont là pour t'aider mais il faut arrêter de penser en Java où (presque) tous est permis et penser de la sécurité et fiabilité de ton code.
J'ai noté que, souvent tu manques les try catch autour plusieurs APIs de GRDB. Regardes bien le code exemple et lis le code source et tu y trouveras où il faut les utiliser. Encore une fois, c'est pour ta protection.
Bah non ! Tu penses de recommencer avec encore un framework ? Tiens à ce que tu as déjà appris et le maitriser ; la plupart de tels frameworks sont écrit de la même manière. C'est plutôt toi qui dois changer, pas les frameworks :-*
Ce n'est pas Swift qui n'est pas user friendly, c'est plutôt SQL, surtout dans le monde de programmation d'aujourd'hui avec les objets, classes, structs, type-safety, etc. Les frameworks doivent être compliqués à raison qu'il doivent traduire le monde de SQL vers le monde des objets.
--
Mais, dis moi : tu parles du dynamisme mais, surement, les tables en SQL ne sont pas dynamiques. Leurs structures sont figées au moment de conception, comme les structs et les classes.
Si tu veux vraiment rester avec la BDD SQLite, tu pourrais créer une struct par table ; tu connais le structure de chaque table, il ne reste que les "copier" en structs. Après ça, quand tu récupères les données d'une table, il ne reste qu'à créer une liste d'instances de la struct qui corresponde. Oui, il faudrait créer une méthode pour chaque table mais ce n'est pas difficile a faire.
merci
je vais lire et digéré toutes tes infos.
Pour le coté difficile ou non de créer une méthode pour chaque table/objet, aucun soucis, mais c'est surtout répétitif... donc pas "objet style" une classe qui créer tout ça de façon dynamique, voilà mon idée.
bref, pour te répondre, la structure d'une base de données est bien définit/réfléchi en amont, MAIS ! peut évolué, ça on le vois bien dans le temps, besoin du client etc. Voilà pourquoi je voulais m'amusé à ça, pour comprendre. Et c'est inconcevable d'aller remettre les main dans le code tout le temps...
Si tu regardes dans le source de GRDB, tu y trouveras beaucoup de répétitif ; ça, c'est la nature des classes/structs adaptatrices.
Il y a pas mal de fonctionnalités que tu n'as pas encore touché dans GRDB ; il te faut l'étudier.
Là tu as déjà le concept d'un dictionnaire assez sophistiqué dans la classe Row ; la méthode value(named:_) renvoie un objet qui implémente le protocol DatabaseValueConvertible, qui te donne le type de chaque valeur comme un enum, avec lequel tu peux vérifier et convertir la valeur selon tes besoins.
Tu pourrais bien créer une struct/classe qui s'occuperait des conversions mais, là dedans, tu devras écrire le code qui fasse les conversions pour chaque table "à la main"
Si tu écrivais bien tes structs/classes pour les conversions, modifier le code pour les colonnes additionnelles ne mettra pas beaucoup de temps.
à‰tudes, études, études 8--)
j'ai enfin réussi, mais je dois à chaque fois préciser quelle colonne je veux afficher(dans le sens ou si je précise pas ce que je souhaite, je pourrais pas itérer !! )... bref, voici le dernier code :
Mais je ne comprends pas ou placer le refresh des données affichés dans la tableView :
j'ai tenté plusieurs essais en vain...
Et j'obtient une concaténation des données dans la tableView...
Si vous pouviez juste me dire votre ressenti, je trouve pas ça très jolie/clair/propre
En regardant mon code :
vous constatez que je perds la référence des datas donc je les place dans ma vue dans un autre tableau, j'ai trouvé que cette solution pas très élégante...
Merci
La tableView ne contient aucune donnée, Dans l'action pour le bouton, il ne faut que changer la source des données, comme je t'ai déjà montré.
Je ne comprends pas ce que tu me dis :
Elle fonctionne déjà et sans le passer par le storyboard. (ça change quoi ? Via le code je suis maitre de ce que je fais, je peut plus intervenir que via l'interface graphique qui (je trouve, dans mon cas présent) surcharge la vision d'ensemble...
Pourtant, à présent, ma tableView est bien remplis avec les bonnes données, c'est "juste" au niveau du reload que j'ai un soucis (pour la changer les datas à chaque pression d'un nouveau bouton. Et ma data source est pourtant bien affecter comme suis :
Quand tu me dis "ce que je t'ai montré" tu parles des tes 5 méthodes (boutons) et leurs affectation de la data source ?
Parce que, tout est identique à ton idée mais sans la répétition et les plusieurs tableau, moi j'en ai juste un du coup :
Tout ça fonctionne bien dans mon code et ça via une boucle qui construit mes boutons à la volé.(Et comme tu le vois je n'ai aucun soucis avec la récupération de mes senders)
Donc, désolé, mais je comprends pas trop ou tu veux en venir ?
à‰videmment.
Mais la méthode est appelé combien de fois ?
Oui
Est c'est là ton problème.
********** Là , tu ajoutes la tableView à sa superView.
Chaque fois que tu passes par la boucle, tu crées une UITableView, tu mets self comme dataSource, tu mets self comme delegate, tu l'ajoutes à sa superView qui la garde à jamais et, c'est quand que tu la supprimes de sa superView ? Du coup, après 10 passes par la boucle, tu as 10 tableViews, tous connectées à son dataSource te delegate - c'est fou !
C'est pour les raisons comme ci que je t'ai conseillé de ne pas le créer en code. Tu n'as aucun besoin de remplacer (dans ton cas ajouter une deuxième, troisième, etc) ; il ne faut que changer la liste que tu utilises comme dataSource.
Et, en plus, chaque fois que tu passes par la boucle, tu ajoutes les prochaines données au même liste qu'avant que as appuyé sur le bouton. Du coup, tu une liste qui s'agrandisse chaque fois que l'on appuie sur un bouton.
Il ne m'étonne pas que ne vois de change dans la/les tableView(s)
Mais, tout ça ne fonctionne pas bien ; si ça fonctionnait bien, tu n'aurais pas de soucis avec le reloadData()
Mais encore ??
Une seule fois, je ne peut pas appuyer sur plusieurs boutons (en tout ça pas là )...
Chaque fois que appuies sur un bouton, tu fais ce code :
Du coup, tu crées une UITableView, tu connectes son dataSource et son delegate à self (le viewController) et tu l'ajoutes comme subView de ta self.view. Dès ce moment, self.view retient la tableView et, la prochaine fois que tu appelles ce code, tu crées une autre aux même coordonnées, cette tableView couvrira la précédente afin que tu ne la puisses voir, mais elle reste là tout même, demandant les données par son dataSource.
Mais, tu as raison en disant qu'il faut appeler reloadData() au fin du code du bouton.