[RESOLU] Problème liste "Select" en swift ?

InsouInsou Membre
juin 2016 modifié dans API UIKit #1

Bonjour,


 


Ayant fait un peu le tour du forum, j'ai l'impression qu'il y a des gens assez compétents ici pour pouvoir m'aider (et en français aussi, ça fait plaisir ^^)..


 


J'ai un petit soucis de conversion de type et je ne sais pas comment le résoudre..


 


En gros, j'utilise Eureka pour créer un formulaire, dedans j'ai une liste dont je récupère les valeurs en json (j'utilise SwiftyJson pour manipuler tout ça..)


Le soucis, c'est que je n'arrive pas à  mettre les valeurs de la liste que je reçois en json, dans le "PushRow" de mon formulaire.. en gros, j'ai pas les valeurs de ma liste quand je clic dessus..


 


Je vais vous détaillez le code, ça sera peut-être plus parlant..


 


Le json que je reçois :



{
"listValue": [{
"id": 1,
"value": "Value 1"
},{
"id": 2,
"value": "Value 2"
},{
"id": 3,
"value": "Value 3"
}]
}

1/ J'ai déclaré une structure pour bloquer les valeurs id et value : 



struct MyStruct {
var id: Int
var value: String

init(id: Int, value: String) {
self.id = id
self.value = value
}
}

2/ Je l'ai rendu "Equatable"



struct MyStruct {
var id: Int
var value: String

init(id: Int, value: String) {
self.id = id
self.value = value
}
}

extension MyStruct: Equatable {}

func ==(lhs: MyStruct, rhs: MyStruct) -> Bool {
let areEqual = lhs.id == rhs.id &&
lhs.value == rhs.value

return areEqual
}

3/ Puis CustomStringConvertible



struct MyStruct : CustomStringConvertible {
var id: Int
var value: String

init(id: Int, value: String) {
self.id = id
self.value = value
}

var description: String {
return "\(self.id)"+" "+"\(self.value)"
}

}

extension MyStruct: Equatable {}

func ==(lhs: MyStruct, rhs: MyStruct) -> Bool {
let areEqual = lhs.id == rhs.id &&
lhs.value == rhs.value

return areEqual
}

J'ai fais tout ça car c'est ce qu'on m'a conseillé sur un autre forum..


 


J'ai donc essayé de mettre les valeurs que je récupère en json dans mon "PushRow" et là , ça ne passe pas..



<<< PushRow<MyStruct>(idItem) {
$0.title = item["libItem"].stringValue
$0.options = item["listValue"].dictionaryValue // Erreur ici

Je me retrouve avec un message d'erreur me disant : Cannot assign value of type '[String : JSON]' to type '[MyStruct]'


 


Ma "listeValue" est un objet SwiftyJSON..


Comment la convertir pour la rendre compatible avec ma structure (MyStruct) ?


 


Est-ce-que je me dirige dans le bon sens pour afficher une liste "dynamique" ?


Si ça se trouve, il y a une façon de faire beaucoup plus simple ?


 


Merci de votre aide :)


Réponses

  • InsouInsou Membre

    Bon, j'ai revu mon code et fait quelque chose de plus simple.. ça aidera peut-être à  mieux comprendre le soucis..


     


    En gros, j'utilise un PushRow (d'Eureka)



    var Liste : [String] = []
    for (idvaleur,TabValeur):(String, JSON) in item["listValue"] {
    print(TabValeur["id"])
    print(TabValeur["value"])
    print("#######")
    Liste.append(TabValeur["value"].stringValue)
    }

    print(Liste)

    form.last! // recupère la dernière section
    <<< PushRow<String>(idItem) {
    $0.title = item["libItem"].stringValue
    $0.options = Liste
    }

    Mon json listValue est toujours le même..


     


    Du coup, c'est bon, j'ai bien mes valeurs dans la liste, je peux choisir celle qui me convient, etc etc..


    Le soucis vient à  la validation du formulaire, je me retrouve avec le texte (value) sélectionné et pas la valeur (id).. 


    Ce qui est normal car je ne sais pas comment l'intégré dedans.. mais je voyais plus une utilisation comme la balise "select" en html.. 



    <select>
    <option value="1">Valeur 1</option>
    <option value="2">Valeur 2</option>
    </select>

    Et que la validation du formulaire me retourne 1 ou 2 .. et pas le libellé..


     


    Bref, du coup, est-ce-que quelqu'un à  une idée de comment faire ça ?


    Ou j'utilise peut-être pas les bons trucs pour faire des formulaires simplement ?


    Qu'utilisez-vous pour faire des formulaires assez complets ?


     


    Merci de votre aide :)


  • CéroceCéroce Membre, Modérateur

    Personnellement, ce n'est pas que je n'ai rien à  cirer de ton problème, mais je n'ai jamais utilisé Eureka, alors j'ignore comment il s'attend à  ce qu'on convertisse les objets JSON en struct.


  • InsouInsou Membre

    Oui c'est le soucis avec ce projet, c'est que j'utilise plusieurs trucs ensemble (SwiftyJson, Eureka, etc etc), du coup ça rend le problème bien spécifique et je ne trouve pas beaucoup de personne dans mon cas..


     


    C'est aussi pour ça que j'ai simplifié mon code, essayé de rendre le truc un peu plus générique dans mon deuxième post.


     


    Du coup, as-tu un exemple de listebox faite en swift ?


    Qu'utilise tu pour faire tes formulaires avec des règles de vérification ? 


    Je me dis qu'il y a peut-être un truc plus simple dont je ne connais pas l'existence..


  • FKDEVFKDEV Membre

    Quand tu fais ça :



    <<< PushRow<MyStruct>(idItem) {


    Tu dis que la donnée associé à  ta ligne sera de type MyStruct. Le problème c'est qu'ensuite tu passes une donnée de type Dictionary ici, alors que tu devrais passer un tableau de MyStruct



    $0.options = item["listValue"].dictionaryValue // Erreur ici

    Après je ne connais pas eureka, donc je ne sais pas comment il va transformer MyStruct en un texte à  afficher. Logiquement MyStruct devrait satisfaire à  un protocole qui lui permettent de récupérer le texte à  afficher pour une valeur donnée.


    Swift possède déjà  un tel protocole qui s'appelle CustomStringConvertible



    protocol CustomStringConvertible {
    var description: String { get }
    }

    Donc, je suppose que si tu implémente CustomStringConvertible dans MyStruct et que tu retournes un texte correpsondant à  ton item, cela va surement fonctionner.

  • InsouInsou Membre

    Je voudrais bien lui passer un tableau de "MyStruct" sauf que je ne sais pas comment faire, pour moi c'est encore un peu flou de ce côté là  :s


     


    Pour le CustomStringConvertible, c'était déjà  le cas dans mon premier post nan ? (le point 3)


  • Je dirais que le tableau [MyStruct] doit être assigné à  $0.options. La valeur de $0.value contient la valeur par défaut représenté par une valeur "MyStruct"


     


    Pour repartir de ton premier exemple:



    let myStructArray: [MyStruct] = <création du tableau>
    <<< PushRow<MyStruct>("TagItem") {
    $0.title = "Titre de la ligne"
    $0.options = myStructArray
    $0.value = myStructArray[0] // Option par défaut

    Je ne connais pas Eureka, mais en regardant en diagonale la documentation c'est ce que je ferais. Ensuite je supposes que tu récupères la valeur sélectionné par l'utilisateur (qui est représenté par un object de type MyStruct).


    Le fait de rendre la structure CustomStringConvertible permet d'afficher à  l'utilisateur une chaine représentative de ce que contient MyStruct. Je supposes que dans ton cas tu pourrais simplement retourner "value".


     


    Tout cela "À une vache près, hein" â„¢Karadoc.


     


    Ha oui n'oublie pas de laisser un petit post dans la section présentation des membres (sauf si c'est déjà  fait).

  • FKDEVFKDEV Membre
    juin 2016 modifié #8


     


    Pour le CustomStringConvertible, c'était déjà  le cas dans mon premier post nan ? (le point 3)




     


    yep.   J'avais pas vu.

  • FKDEVFKDEV Membre
    juin 2016 modifié #9

    Je n'ai pas compris immédiatement leur système de boucle,




    //If json is .Dictionary
    for (key,subJson)String, JSON) in json {
    //Do something you want
    }

    The first element is always a String, even if the JSON is an Array

    //If json is .Array
    //The `index` is 0..<json.count's string value
    for (index,subJson)String, JSON) in json {
    //Do something you want
    }


     


    mais j'ai trouvé une autre manière de le faire en cherchant un peu (en anglais) :



    var arrayOfMyStruct:[MyStruct]
    for item in json["listValue"].arrayValue {
    arrayOfMyStruct.append(MyStruct(item["id"].intValue, item["value"].stringValue))
    }

    C'est un cas où le map pourrait avantageusement remplacer le for, mais le for est plus pédagogique.


  • En tous cas ce sont deux frameworks qui, s'ils font gagner un peu de temps (ça reste à  prouver), ne font pas gagner en compréhension de Swift et de la programmation.


  • InsouInsou Membre
    juin 2016 modifié #11

    Tout cela "À une vache près, hein" â„¢Karadoc.



     


    Bon et bien à  une vache près, ça fonctionne ^^


    J'arrive donc au même résultat (en utilisant une structure) que mon deuxième post (avec un tableau de string).


    Je trouve la méthode avec une structure mieux, à  mon avis, j'vais pouvoir faire plus de chose avec ça..


     


    Je remet le code ici, si ça peut en aider certains :



    struct MyStruct : CustomStringConvertible {

    var id: Int
    var value: String

    init(id: Int, value: String) {
    self.id = id
    self.value = value
    }

    var description: String {
    //return "\(self.id)"+" "+"\(self.value)"
    return "\(self.value)"
    }

    }

    extension MyStruct: Equatable {}

    func ==(lhs: MyStruct, rhs: MyStruct) -> Bool {
    let areEqual = lhs.id == rhs.id &&
    lhs.value == rhs.value
    return areEqual
    }


    var arrayOfMyStruct:[MyStruct] = []
    for item in item["listValue"].arrayValue {
    print(item["id"].intValue)
    print(item["value"].stringValue)
    print("#############")
    arrayOfMyStruct.append(MyStruct(id: item["id"].intValue, value: item["value"].stringValue))
    }

    form.last! // recupère la dernière section
    <<< PushRow<MyStruct>(idItem) {
    $0.title = item["libItem"].stringValue
    $0.options = arrayOfMyStruct
    }

    En fait, maintenant que j'ai le code sous les yeux, je comprends mieux comment on définit un tableau de structure personnalisée et comment le remplir, je bloquais dessus, du coup merci, j'suis sur que ça me resservira ;)


     


    Ensuite, j'ai encore le deuxième soucis qui persiste..


    J'ai donc ma liste où je peux choisir mes données (recup' via le json) mais quand je valide le formulaire, je me retrouve avec le texte sélectionné et pas l'id de la valeur.. ce qui est assez embêtant..


    Il y a surement un truc que je ne connais pas sur les listes, les structs ou quoique ce soit d'autre pour que ça m'affiche le texte à  l'affichage mais que ça renvoi l'id à  la validation du formulaire.. .. .. nan ?


    Je vois mal ce genre de truc (le fait que ça renvoi le texte affiché) comme comportement par défaut dans une appli..


     


     



     


     


    En tous cas ce sont deux frameworks qui, s'ils font gagner un peu de temps (ça reste à  prouver), ne font pas gagner en compréhension de Swift et de la programmation.

     


    J'ai pas encore assez de recule pour ça mais je te crois sur parole ^^


     


    En tout cas, merci à  vous 2 pour votre aide :)




  •  


    Ensuite, j'ai encore le deuxième soucis qui persiste..


    J'ai donc ma liste où je peux choisir mes données (recup' via le json) mais quand je valide le formulaire, je me retrouve avec le texte sélectionné et pas l'id de la valeur.. ce qui est assez embêtant..


    Il y a surement un truc que je ne connais pas sur les listes, les structs ou quoique ce soit d'autre pour que ça m'affiche le texte à  l'affichage mais que ça renvoi l'id à  la validation du formulaire.. .. .. nan ?


    Je vois mal ce genre de truc (le fait que ça renvoi le texte affiché) comme comportement par défaut dans une appli..


     




     


    Comment récupères tu la valeur de la ligne ?


     


    Depuis ton code idItem étant un tag je ferais un truc du genre



    if let rowValue = form.rowByTag(idItem)?.value as? MyStruct {
    print(rowValue.id)
    }

  •  


    Comment récupères tu la valeur de la ligne ?



     


    Ah oui, j'suis con, j'ai même pas mis le code  :*


     


    Du coup, c'est Eureka qui récupère toute les valeurs de mon formulaire..



    let dataValid = self.form.validateAll() // vérifie les règles sur les items (du genre, doit être un nombre, un email, ne doit pas être vide, etc etc

    if dataValid {
    //print("Formulaire valide")
    let valeursFormulaire = self.form.values(includeHidden: true) // true pour recup les valeurs cachées
    //print(valeursFormulaire)

    for (idvaleur,valeur):(String, Any?) in valeursFormulaire {
    if let laValeur = valeur {
    print("\(idvaleur)"+" "+"\(laValeur)")
    }
    }
    } else {
    print("Formulaire invalide")
    }

    La fonction form.values() ressemble à  ça :



    public func values(includeHidden includeHidden: Bool = false) -> [String: Any?]{
    if includeHidden {
    return allRows.filter({ $0.tag != nil })
    .reduce([String: Any?]()) {
    var result = $0
    result[$1.tag!] = $1.baseValue
    return result
    }
    }
    return rows.filter({ $0.tag != nil })
    .reduce([String: Any?]()) {
    var result = $0
    result[$1.tag!] = $1.baseValue
    return result
    }
    }

    A vu de nez, il n'y a pas l'air d'avoir d'option pour choisir ce qu'on veut récupérer :/


    J'ai l'impression que je suis bon pour modifier leur framework et j'aime pas vraiment ça :s


  • LexxisLexxis Membre
    juin 2016 modifié #14

    Dans la boucle for (idvaleur, valeur)... valeur est du type Any?. As tu vérifié que dans le ton cas que valeur n'est pas du type MyStruct justement ? (tu peux peut être l'avoir avec un print("\(laValeur.self)")) (ou dynamicType à  la place de self).


  • InsouInsou Membre
    juin 2016 modifié #15

    Alors avec self.dynamicType, valeur est du type MyStruct ..


     


    Du coup, j'ai modifié ma fonction de récupération des valeurs comme ça : 


                     



    for (idvaleur,valeur):(String, Any?) in valeursFormulaire {
    if let laValeur = valeur {

    let typeDeChamps = String(laValeur.dynamicType)

    switch(typeDeChamps){

    case "MyStruct":
    if let rowValue = self.form.rowByTag(idvaleur)?.baseValue as? MyStruct {
    print("\(rowValue.id)")
    }

    default:
    print("\(idvaleur)"+" "+"\(laValeur)")
    }
    }
    }

    Du coup, je récupère bien l'id de la valeur et c'est nickel  :D


    Je vois maintenant comment mieux utiliser les structures et récupérer leur valeur, ça me sera bien utile ^^


     


    Grand merci à  vous 2  o:)


  • L'explication c'est que quand tu fait :



    print("\(value)")

    Cela utilise le protocole CustomStringConvertible...




  • L'explication c'est que quand tu fait :



    print("\(value)")

    Cela utilise le protocole CustomStringConvertible...




     


    Ouai, j'me suis rendu compte de ça ^^


    D'où l'utilisation du tag pour récupérer le champ, puis la valeur qui m'intéresse 

Connectez-vous ou Inscrivez-vous pour répondre.