[SWIFT] Problème de taille avec des éléments dynamiques, autolayout ?

InsouInsou Membre

Nouvelle journée, nouveau problème :D

Je vais essayé de résumer simplement mon soucis..

J'ai une vue scrollable qui doit ressembler à ça :

Le soucis se trouve sur la vue "Qui voulez-vous prévenir ?"

Les 2 premiers choix sont statiques, les 2 autres sont une liste que je récupère en JSON

Là il y a que 2 groupes mais il pourrait y en avoir plus..
Vous voyez venir le problème ? :D

Comment faire pour ajouter une liste de groupe et que la taille de mon écran se redimensionne toute seule ?
Pour l'instant mes tailles sont fixes et j'ai l'impression que c'est pas la bonne manière de faire..

Quelqu'un aurait une piste pour me débloquer ? ^^

Merci de votre aide :)

Réponses

  • T'as pensé à une TableView ?

  • InsouInsou Membre

    Ouai mais la tableView va me faire un 2eme ascenseur si il y a trop de groupes à rajouté.. j'aimerai éviter ça ^^

    Après je ne sais pas si c'est possible d'agrandir la tableView en fonction des données dedans..

  • LarmeLarme Membre
    22 févr. modifié #4

    Sauf si la size de l'UITableView est la même que sa contentSize.

    De mémoire, tu peux subclasser UITableView, override le didSet du contentSize et updater sa size (via contrainte, ou autre).

  • Joanna CarterJoanna Carter Membre, Modérateur
    22 févr. modifié #5

    Je dirais l'UITableView avec les cellules dédiées pour chaque section. Je l'utilise pour mon app Fest Jazz. L'écran des événements contient une journée et chaque journée contient plusieurs événements.

    N'oublies pas que tu peut utiliser les cellules statiques ou dynamique dans la même UITableView

    @Insou a dit :
    Après je ne sais pas si c'est possible d'agrandir la tableView en fonction des données dedans..

    Il ne faut qu'appeler reloadData pour ça

    Petit exemple de code pour gérer deux types de cellules :

    extension ArtistViewController : UITableViewDelegate
    {
      func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String?
      {
        switch section
        {
          case 0:
            return nil
          default:
            guard let events = self.artist?.events,
                  events.count > 0 else
            {
              return nil
            }
    
            return Localisation.representations.localised
        }
      }
    
      func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat
      {
        return indexPath.section > 0 ? ArtistEventCell.rowHeight : UITableViewAutomaticDimension
      }
    
      func tableView(_ tableView: UITableView, willDisplayHeaderView view: UIView, forSection section: Int)
      {
        guard let headerView = view as? UITableViewHeaderFooterView else
        {
          return
        }
    
        headerView.backgroundView?.backgroundColor = tableView.backgroundColor?.withAlphaComponent(0.8)
    
        headerView.textLabel?.textColor = UIColor.festJazzBlue
      }
    }
    
  • InsouInsou Membre

    @Larme La size de mon UITableView est plus petite que la taille de celle qui l'a contient..
    J'ai des marges sur les côtés et j'ai un label au dessus.

    T'as un exemple de comment faire ça ?
    Je vais orienter mes recherches dans ce sens..

    @Joanna Carter Je n'ai pas le droit aux sections, tout doit être visible directement comme sur le screen :/

  • Joanna CarterJoanna Carter Membre, Modérateur
    22 févr. modifié #7

    @Insou a dit :
    @Joanna Carter Je n'ai pas le droit aux sections, tout doit être visible directement comme sur le screen :/

    Si le nombre de cellules est trop pour l'écran, tu devras scroller sans ou avec sections

  • InsouInsou Membre

    Si le nombre de cellules est trop pour l'écran, tu devras scroller sans ou avec sections

    Oui mais j'aimerai éviter le double ascenseur..
    Dans tout les cas, ma page est déjà trop grande pour un écran et il va falloir scroller pour arriver en bas.
    J'aimerai éviter qu'on scroll aussi pour choisir les groupes.

    Pour la view "Que se passe-t-il ?", je devrais la mettre dans le tableView ?

    Ce qui est intéressant dans ton schéma c'est la partie "Qui voulez vous prévenir ?"
    C'est pas bête de mettre des header et footer, j'y avais pas pensé.

    Mais ça ne règle pas mon problème de la taille de ma tableView..
    Comment faire pour que la taille de ma tableView soit de la taille de mes groupes (dynamiques) récupéré en JSON ?
    Si j'en ai 10, je veux juste qu'ils soient affichés les uns sous les autres et qu'on ait pas à scroller dans la tableView, comme ça, pas de double ascenseur.. pas grave si on doit scroller beaucoup, tant que c'est visuel et pratique :)

  • Joanna CarterJoanna Carter Membre, Modérateur
    22 févr. modifié #9

    Tu renvoies le nombre de groupes dans numberOfSections dans le delegate

  • Tu devrais pouvoir faire du KVO sur la propriété contentSize de ton UITableView.

    Donc en mettant un contrainte de hauteur sur ton UITableView, dès qu'il y a le changement détecté via le KVO, update la valeur de contrainte.

    Comme sa contentSize sera la même que sa taille, elle n'est pas "scrollable" normalement. Il faudra peut-être désactiver le bounce, ou le scroll dessus. Dès lors, elle n'aura pas d'ascenseur.

  • Je revenais posté une solution que j'ai trouvé pendant le week-end et je crois que ça ressemble le plus à la solution de Larme ^^

    En gros, j'ai mis une contrainte de hauteur sur ma tableView, je l'ai déclaré :

    @IBOutlet weak var heightTableView: NSLayoutConstraint!
    

    Et lorsque je reçois mes groupes en JSON, je réajuste la taille de ma tableView en calculant combien j'ai de ligne.
    Je fixe une hauteur de ligne aussi.

    let hauteurLigne = 50
    let height = CGFloat((groupes.count*hauteurLigne))
    tableViewGroupe.rowHeight = CGFloat(hauteurLigne)
    heightTableView.constant = height
    

    J'ai testé ça sur un nouveau projet, je vais voir si ça se passe aussi bien quand je l'intègre à mon projet en cours mais normalement ça devrait le faire.

  • Hors sujet mais j'avoue que le terme "incident" avec attentat ou explosion m'a fait un poil marrer :D
    Ca ne serait pas mieux de mettre "événement" qui correspond plus à un problème de premier ordre plutôt que de considérer ça comme secondaire ?

    Après chacun sa vision du monde hein ;)

  • @Harlo a dit :
    Hors sujet mais j'avoue que le terme "incident" avec attentat ou explosion m'a fait un poil marrer :D
    Ca ne serait pas mieux de mettre "événement" qui correspond plus à un problème de premier ordre plutôt que de considérer ça comme secondaire ?

    Après chacun sa vision du monde hein ;)

    Dans le milieu de la sureté/sécurité.. un incident c'est pas secondaire ;)

  • @Insou a dit :
    Dans le milieu de la sureté/sécurité.. un incident c'est pas secondaire ;)

    C'est clair qu'un événement nucléaire n'a pas la même charge émotionnelle qu'un incident nucléaire ..

  • @Insou a dit :
    Je revenais posté une solution que j'ai trouvé pendant le week-end et je crois que ça ressemble le plus à la solution de Larme ^^

    En gros, j'ai mis une contrainte de hauteur sur ma tableView, je l'ai déclaré :

    @IBOutlet weak var heightTableView: NSLayoutConstraint!
    

    Et lorsque je reçois mes groupes en JSON, je réajuste la taille de ma tableView en calculant combien j'ai de ligne.
    Je fixe une hauteur de ligne aussi.

    let hauteurLigne = 50
    let height = CGFloat((groupes.count*hauteurLigne))
    tableViewGroupe.rowHeight = CGFloat(hauteurLigne)
    heightTableView.constant = height
    

    J'ai testé ça sur un nouveau projet, je vais voir si ça se passe aussi bien quand je l'intègre à mon projet en cours mais normalement ça devrait le faire.

    Cela ne fonctionne que si tu connais la hauteur des rows. Et cela ne rajoute pas les headers/footers, non ?

    Ce dont je parlais:

    var observer: NSKeyValueObservation?
    observer = tableView.observe(\UITableView.contentSize, changeHandler: { [weak self] (table, value) in
       //Peut-être qu'un `DispatchQueue.main.asyn{}` doit être fait
        if let newHeight = value.newValue?.height { 
            heightTableView.constant = newHeight
        }
    })
    
  • Bah je peux connaitre la hauteur des rows donc ça ne me pose pas de soucis.

    En effet, pas de headers/footers mais cette solution de headers/footers, c'était parce que je n'arrivais pas a avoir la bonne taille de ma tableView.. maintenant que c'est le cas, je n'ai plus besoin des headers/footers.

    Par contre, pour le coup, je ne comprends pas pourquoi personne ne m'a proposé la solution de la contrainte à recalculer ^^
    C'est de loin la plus simple et qui colle parfaitement à ce que je voulais :p

    (Bon ok, peut-être que personne n'y a pensé aussi lol)

  • Trop statique, trop spécifique, à adapter. Et il fallait savoir que tes cellules avaient obligatoirement la même taille.
    Ma solution avec KVO devrait gérer ces cas-là.
  • Pour l'instant j'ai fais au plus simple avec la taille de ma contrainte et ça fonctionne ^^

    Du coup, question suivante :D

    Sur chaque ligne, j'ai un Switch
    J'arrive bien à détecter le changement d'état, quand je le passe à On/Off mais je n'arrive pas à savoir sur quelle ligne de ma tableView je suis..

    En gros, à chaque fois que j'ai un switch à On, je voulais ajouté la ligne dans un tableau qui me servira à savoir à la fin, quels groupes sont cochés.
    Et la supprimer quand il est à Off bien sur.

    Du coup, comment savoir sur quelle ligne mon switch est lorsqu'il change d'état ?

    Je dois avoir trop la tête dans le guidon pour pas trouver parce que ça me parait simple à faire :/

  • InsouInsou Membre

    Bon.. Je ne m'en sort pas avec cette histoire de taille..

    En gros, j'arrive à gérer ma tableview pour y donner la bonne taille pour y recevoir mes groupes (en json) mais si j'ai trop de groupe ou pas assez, du coup c'est ma scrollview (ou la taille de ma view principale) qui ne suis plus..

    Dans mon code, je reçois plusieurs "groupes" mais je n'arrive pas à régler la bonne taille de ma vue après..

    J'ai fais un prototype d'où je bloque (en pièce jointe), ça sera peut être plus simple pour comprendre..
    Si quelqu'un à 5 minutes pour regarder ça de plus prêt, ça me débloquerai parce que là, je ne sais plus quoi faire :/

    Merci de votre aide :)

    Test.zip 282.1K
  • LarmeLarme Membre

    Alors rapidement:

    Il faut une contrainte entre ta vue la plus en bas (celle où se trouve le bouton) et la superview (la orange/marron).
    J'ai mis Bottom space >= 0 et j'ai supprimer la superview hauteur fixe.

    Après, pour jouer plus rapidement (et de manière un peu sale j'avoue):

    J'ai remplacé :

    var groupes : [String] = ["Groupe 1", "Groupe 2", "Groupe 3"]
    

    Dans cellForRow:

    cell?.textLabel?.text = model
    

    Et dans clicButton:

    groupes.append("Groupe \(groupes.count)")
    tableViewGroupe.reloadData()
    let hauteurLigne = 50
    let height = CGFloat((groupes.count*hauteurLigne))
    print(height)
    tableViewGroupe.rowHeight = CGFloat(hauteurLigne)
    heightTableView.constant = height
    
  • InsouInsou Membre

    j'ai supprimer la superview hauteur fixe.

    Qu'est ce que tu appelles "la superview hauteur fixe." ? :/

    J'ai supprimé la contrainte de hauteur fixe de ma superview (orange/marron) mais je me retrouve avec une erreur

    Scroll View need constraints for : Y position or height.
    

    Du coup, je suis pas sûr d'avoir bien compris ou bien supprimé le bon truc :/

  • InsouInsou Membre

    Cette histoire me rend fou :D

    J'ai refait un prototype, un peu plus fonctionnel que le premier en essayant d'appliquer les conseils de Larme.

    J'ai donc maintenant mon bouton en bas qui colle bien avec le bas de la view, c'était pas le cas avant.

    J'ai bien ma tableView pour recevoir mes groupes qui s'agrandie en fonction de mes groupes.. sauf que.. arrivé au delà de 6 groupes.. ma tableView ne s'agrandie plus et m'ajoute une scrollbar pour voir le reste des groupes ajoutés o_O

    Si quelqu'un a 2 minutes pour jeter un oeil à mon soucis (en pièce jointe), ça me débloquerai, j'arrive à court d'idées là :/

    Merci de votre aide :)

    Test.zip 287.4K
  • LarmeLarme Membre

    Il suffit de lire les logs.
    Quand tu arrives à un certains nombre de rows, tu vois clairement dans les logs:

    Will attempt to recover by breaking constraint 
    <NSLayoutConstraint:0x600001634ff0 UITableView:0x7f9070042c00.height == 350   (active)>
    

    Donc plutôt que >=0 comme je l'avais dit, mets = 0.
    Plutôt que de dire que la vue bouton est à Y du top (ce qui posait problème et d'où les contraintes cassaient), dis qu'elle doit être à Y de la vue juste au-dessus.

    Derniers conseils :
    N'intègre pas SwiftyJSON comme ça, utilises CocoaPods.
    SwiftyJSON ne devrait pas être utilisé partout, il doit être utilisé uniquement quand tu reçois la réponse de ta requête, après, tu dois toujours avoir un objet/struct à toi.
    D'un autre côté, tu sembles faire du Swift 4.2, préfères Codable.

    Évite vraiment d'avoir un mélange français anglais.

    Pense à celui qui arrivera après toi pour maintenir l'application.

    Test.zip 370.1K
  • InsouInsou Membre

    Plutôt que de dire que la vue bouton est à Y du top (ce qui posait problème et d'où les contraintes cassaient), dis qu'elle doit être à Y de la vue juste au-dessus.

    En fait, j'avais juste une mauvaise contrainte qui m'embrouillait >_<
    Je viens de tester, ça fonctionne bien.
    J'y étais presque :cry:

    N'intègre pas SwiftyJSON comme ça, utilises CocoaPods.

    Le soucis avec CocoaPods, c'est que parfois ça fonctionne bien, parfois nan.
    Du coup, quand je cherche à intégrer des librairies externes, si je peux les glisser/déposer dans le projet, je le fais, ça fonctionne et voila.
    Mais je me doute qu'il doit y avoir un intérêt particulier pour utiliser CocoaPods (que j'ai pas encore vraiment bien saisie) et je dois parfois mal m'en servir, c'est sûr car parfois j'essaie d'intégrer une librairie via CocoaPods et ça foire dans tout les sens (j'ai plus les erreurs mais à l'occasion je referai le test) et parfois ça fonctionne très bien.

    SwiftyJSON ne devrait pas être utilisé partout, il doit être utilisé uniquement quand tu reçois la réponse de ta requête, après, tu dois toujours avoir un objet/struct à toi.

    Oui oui, c'est juste que là, je l'utilisais dans mon code de base et quand j'ai refait le prototype pour mes tests, je l'ai réutilisé pour coller le mieux à mon code.
    Mais en effet, je l'utilise que pour la réception de ma requête dans mon "vrai code" ^^

    Comme dab', merci pour ton aide Larme :)

    Je retourne refaire ça au propre sur le vrai projet et normalement ça devrait le faire :D

  • CéroceCéroce Membre, Modérateur

    @Insou a dit :
    Le soucis avec CocoaPods, c'est que parfois ça fonctionne bien, parfois nan.

    Ça marchait mal il y a quelques années, mais franchement depuis 3 ans, je n'ai eu aucun souci.
    Il y a Carthage sinon, qui est moins invasif et dorénavant pris en charge par la grande majorité des projets open-sources.

    Mais je me doute qu'il doit y avoir un intérêt particulier pour utiliser CocoaPods (que j'ai pas encore vraiment bien saisie)

    Au moins deux intérêts:

    • les mises à jour des dépendances sont faciles et rapides.
    • ça gère les dépendances de dépendances.

    (Carthage a les mêmes qualités).

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