Question contraintes...

Bonjour à  tous,


 


j'ai besoin de votre avis éclairé pour l'utilisation des contraintes dans une appli


 


cette appli n'a qu'une seule vue


cette vue comporte 170 UIimageView


je souhaite que les proportions et l'ordre de ces 170 UIimageView soient respectées quelque soit le device : iPhone ou iPad


 


je me fiche de la taille des UIimageView je souhaite juste que le design soit le même pour tous les devices


 


voici une idée en image de ce que je souhaite :


 


ok.jpg


 


est ce que c'est les contraintes qu'il faut utiliser ou une autre méthode pour obtenir ce résultat?


 


d'avance je vous en remercie 


Réponses

  • Cela se fait en quelques lignes de code, sans passer par Storyboard ..


  • jeffadsljeffadsl Membre
    novembre 2017 modifié #3

    aurais-tu la gentillesse de me mettre sur une piste ? ( j'ai un niveau très moyen)


     


    UIImageView *dot =[[UIImageView alloc] initWithFrame:CGRectMake(50,50,20,20)];


    pour créer une UIimageView avec des coordonnées ene taille connues


     


    comment faire  avec des proportions utilisables quelque soit la taille et le format de l'écran


     


    un exemple...


  • UICollectionView.


     


    Sinon, j'espère sincèrement que tu ne les as pas toutes mises à  la main.


    En réalité, tu n'as pas forcément besoin des contraintes. Tu connais la taille de la vue/écran, et tu peux setter les frames ainsi dans un for loop.




  • aurais-tu la gentillesse de me mettre sur une piste ? ( j'ai un niveau très moyen)


     


    UIImageView *dot =[[UIImageView alloc] initWithFrame:CGRectMake(50,50,20,20)];


    pour créer une UIimageView avec des coordonnées ene taille connues


     


    comment faire  avec des proportions utilisables quelque soit la taille et le format de l'écran


     


    un exemple...




    J'y travaille. Je te donne ça demain. Par contre, je te préviens ce sera en Swift. D'ailleurs, si tu es novice, tu ferais bien de laisser tomber l'obj-C tout de suite pour passer à  swift.

  • jeffadsljeffadsl Membre
    novembre 2017 modifié #6

    merci beaucoup Darken !!!!!


     


    ok pour le swift 


     


    en attendant de tes nouvelles je m'instruis : https://www.appcoda.com/ios-programming-uicollectionview-tutorial/


     


    pour info je dois pouvoir assigner une couleur a chaque UIimageview ( il faut donc que chacune comporte un nom pour l'ddentifier )


     


    en fait mon appli reçois des infos de couleur pour chaque UIimageView via un server (la partie communication est déjà  faite) comme si chaque UIimageview était un pixel d'écran




  •  


    en fait mon appli reçois des infos de couleur pour chaque UIimageView via un server (la partie communication est déjà  faite) comme si chaque UIimageview était un pixel d'écran




    Tu veux dire que chaque UIImageView ne sert qu'a afficher une seule couleur et non une image ? Dans ce cas, il faut utiliser des UIViews pour ne pas se compliquer la vie.

  • jeffadsljeffadsl Membre
    novembre 2017 modifié #8

    oui que de la couleur


     


     


    voici ce que j'utilise pour le moment :


     


    lastValueled001R


    lastValueled001V


    lastValueled001B


     


    sont les valeurs de couleur reçu par le serveur pour chaque UIViews


    et ça 170 fois...pour chacun de mes pixel



    led001.backgroundColor = [UIColor colorWithRed:(lastValueled001R / 255.0) green:(lastValueled001V / 255.0) blue:(lastValueled001B / 255.0) alpha: 1];


    Ca, c'est ce que j'aimerai ;-))


     


    ok2.jpg


  • jeffadsljeffadsl Membre
    novembre 2017 modifié #9

    Pour le moment j'arrive à  ça, mais la taille est fixe et non proportionnelle aux dimensions de l'écran 


    d'ailleur pour remplir  toute la vue, j'ai du mettre 180 cases alors que je n'en veux que 170


     


    ok3.jpg


     


     


    avec ce code



    #import "ViewController.h"

    @interface ViewController () <UICollectionViewDelegate, UICollectionViewDataSource>

    @end

    @implementation ViewController

    - (BOOL)prefersStatusBarHidden {
    return YES;
    }

    - (void)viewDidLoad
    {
    [super viewDidLoad];
    }

    - (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
    {
    return 1;
    }

    - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
    {
    return 180;
    }

    - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
    {
    UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@cell forIndexPath:indexPath];


    cell. backgroundColor = [ UIColor colorWithRed :(( arc4random ()% 255 )/ 255.0 ) green :(( arc4random ()% 255 )/ 255.0 ) blue :(( arc4random ()% 255 )/ 255.0 ) alpha : 1.0f ];

    cell.layer.borderWidth = 0.5;

    return cell;
    }

    - (UIEdgeInsets)collectionView:(UICollectionView*)collectionView layout:(UICollectionViewLayout *)collectionViewLayout insetForSectionAtIndex:(NSInteger)section {
    return UIEdgeInsetsMake(0, 0, 0, 0); // top, left, bottom, right
    }

    - (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section {

    return 0.0;
    }

    - (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumLineSpacingForSectionAtIndex:(NSInteger)section {
    return 0.0;
    }

    - (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
    {
    UIAlertController *alert = [UIAlertController alertControllerWithTitle:@Cell Tap message:[NSString stringWithFormat:@Cell : %ld selected, indexPath.row + 1] preferredStyle:UIAlertControllerStyleAlert];

    UIAlertAction *okAction = [UIAlertAction actionWithTitle:@Okay style:UIAlertActionStyleDefault handler:nil];
    [alert addAction:okAction];

    [self presentViewController:alert animated:YES completion:nil];
    }

    - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
    return CGSizeMake((collectionView.frame.size.width/10)-0, (collectionView.frame.size.width/10)-0);
    }

    @end




  •  


    Pour le moment j'arrive à  ça, mais la taille est fixe et non proportionnelle aux dimensions de l'écran 


    d'ailleur pour remplir  toute la vue, j'ai du mettre 180 cases alors que je n'en veux que 170


     




     


    C'est normal, ce que tu veux faire est tout simplement impossible. Les différents écrans n'ont pas les mêmes proportions. Tu ne peux pas avoir un jeu de valeurs uniques correspondant à  tous les cas de figure.


     


    Pour remplir l'écran à  chaque fois, il faut forcément faire varier quelque chose : soit le ratio hauteur/largeur des éléments, soit la taille de l'écart x et y les séparant.

  • DrakenDraken Membre
    novembre 2017 modifié #11

    Première version :



    import UIKit

    fileprivate let nbColonnes = 10
    fileprivate let nbLignes = 17

    class ViewController: UIViewController {

    // UIView créé avec Storyboard,
    // dont les dimensions sont calquées sur la SafeArea
    @IBOutlet weak var maVue: UIView!

    // Stockage des cases dans un tableau
    var tableauViews = [UIView]()

    override func viewDidLoad() {
    super.viewDidLoad()

    print ("Taille écran : ", self.view.bounds.size)
    print ("Espace disponible pour les cases : ", maVue.bounds)

    creerUIImagesView()
    }


    func creerUIImagesView() {

    let largeurFenetre = maVue.frame.width
    let hauteurFenetre = maVue.frame.height

    let largeur = largeurFenetre/CGFloat(nbColonnes)
    let hauteur = hauteurFenetre/CGFloat(nbLignes)

    print ("largeur : ", largeur)
    print ("Hauteur : ", hauteur)

    for ligne in 0 ... nbLignes-1 {
    for colonne in 0 ... nbColonnes-1 {
    let rect = CGRect(x: CGFloat(colonne)*largeur,
    y: CGFloat(ligne)*hauteur,
    width: largeur,
    height: hauteur)
    let uneVue = UIView(frame: rect)
    tableauViews.append(uneVue)
    maVue.addSubview(uneVue)
    uneVue.backgroundColor = couleurAleatoire()
    }
    }
    }


    func modifierCouleurCase(numero:Int, couleur:UIColor) {
    tableauViews[numero].backgroundColor = couleur
    }


    func couleurAleatoire() -> UIColor {
    return UIColor(red: CGFloat(arc4random())/CGFloat(UInt32.max),
    green: CGFloat(arc4random())/CGFloat(UInt32.max),
    blue: CGFloat(arc4random())/CGFloat(UInt32.max),
    alpha: 1.0)
    }

    }

    Les cases sont dessinés sur maVue. C'est une UIView que j'ai placé sur l'écran avec Storyboard, avec des contraintes la forçant à  prendre la même dimension que la SafeArea.


     


    Les vues sont stockées dans un tableau, pour y accéder facilement par la suite, pour changer les couleurs. Elles sont numérotées de 0 à  169.


     


    Le ratio hauteur/largeur n'est pas conservé, mais le résultat est pas mal, sur les différents iPhones.


  • CéroceCéroce Membre, Modérateur

    pour info je dois pouvoir assigner une couleur a chaque UIimageview ( il faut donc que chacune comporte un nom pour l'ddentifier )



    Il y a un concours à  celui qui saura gâcher le plus de temps processeur aujourd'hui ?

    UIView.drawRect()




  • Il y a un concours à  celui qui saura gâcher le plus de temps processeur aujourd'hui ?

    UIView.drawRect()




    C'est pas faux !

  • CéroceCéroce Membre, Modérateur
    Et au passage, les storyboards se prêtent très bien à  ce genre de choses.

    Utilisez:
    PlaygroundPage.current.liveView = myView // ou ViewController
    Puis séparez la fenêtre en deux en vous mettant en mode "Assistant Editor", pour avoir le code sur la gauche et l'aperçu sur la droite.
  • DrakenDraken Membre
    novembre 2017 modifié #18

    Pour suivre le conseil de monsieur Céroce, j'ai créé une nouvelle classe servant à  dessiner une grille de cases de couleur.



    class Grille:UIView {

    private var _nbLignes = 0
    private var _nbColonnes = 0

    var couleursCase = [UIColor]()

    // Création objet
    init(frame: CGRect, nbLignes:Int, nbColonnes:Int) {
    super.init(frame: frame)
    self._nbLignes = nbLignes
    self._nbColonnes = nbColonnes

    // Remplissage du tableau de couleurs aléatoire
    for _ in 0 ..< nbLignes*nbColonnes {
    couleursCase.append(self.couleurAleatoire())
    }
    }

    // ça c'est juste pour le compilateur n'hurle pas à  la mort
    // pendant la compilation
    required init?(coder aDecoder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
    }

    // Dessin de la grille
    // iOS utilise cette fonction à  chaque redessin de la vue Grille
    override func draw(_ rect: CGRect) {

    let largeur = rect.width/CGFloat(_nbColonnes)
    let hauteur = rect.height/CGFloat(_nbLignes)

    // Sauvegarde du context graphique initial
    let context = UIGraphicsGetCurrentContext()
    UIGraphicsPushContext(context!)

    // Désactivation anti-aliasing
    // pour avoir des transitions de couleurs nettes
    context?.setShouldAntialias(false)

    var numCase = 0
    for ligne in 0 ..< _nbLignes {
    for colonne in 0 ..< _nbColonnes {
    let rectCase = CGRect(x: CGFloat(colonne)*largeur,
    y: CGFloat(ligne)*hauteur,
    width: largeur,
    height: hauteur)
    // Lecture couleur case
    let couleur = couleursCase[numCase]
    numCase += 1
    // Dessin case
    couleur.set()
    UIRectFill(rectCase)
    }
    }

    // Restitution du context graphique original
    UIGraphicsPopContext()
    }

    private func couleurAleatoire() -> UIColor {
    return UIColor(red: CGFloat(arc4random())/CGFloat(UInt32.max),
    green: CGFloat(arc4random())/CGFloat(UInt32.max),
    blue: CGFloat(arc4random())/CGFloat(UInt32.max),
    alpha: 1.0)
    }

    }


    Le dessin des cases de couleur se fait directement dans la fonction drawRect(), avec des fonctions graphiques de base. Comme le dis l'espèce en voie de disparition pour cause de braconnage, le système n'a pas à  gérer une multitude de sous-vues, ce qui consomme beaucoup moins de mémoire et de temps de calcul.


     


    La couleur des cases est définie dans un tableau de couleurs, accessible à  travers une propriété de la classe Grille. 

  • DrakenDraken Membre
    novembre 2017 modifié #19

    Mise en pratique :



    import UIKit

    fileprivate let nbColonnes = 10
    fileprivate let nbLignes = 17


    class ViewController: UIViewController {

    // Lien vers maVue créée avec des contraintes sous Storyboard
    @IBOutlet weak var maVue: UIView!

    var grille:Grille?

    override func viewDidLoad() {
    super.viewDidLoad()

    self.view.backgroundColor = UIColor.white
    print ("Taille écran : ", self.view.bounds.size)
    print ("Espace disponible pour les cases : ", maVue.bounds)

    // Création de la view Grille
    // (même taille que la vue MaVue)
    let rect = CGRect(x: 0, y: 0,
    width: maVue.bounds.width,
    height: maVue.bounds.height)
    grille = Grille(frame: rect, nbLignes: nbLignes, nbColonnes: nbColonnes)
    if let grille = grille {
    maVue.addSubview(grille)
    }
    }

    // Exemple de modification d'une case de couleur
    // quand l'utilisateur touche l'écran
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {

    // case aléatoire
    let caseAleatoire = Int(arc4random_uniform(UInt32(nbLignes*nbColonnes)))
    grille?.couleursCase[caseAleatoire] = couleurAleatoire()
    // On informe le système qu'il faut redessiner la grille
    grille?.setNeedsDisplay()
    }

    func couleurAleatoire() -> UIColor {
    return UIColor(red: CGFloat(arc4random())/CGFloat(UInt32.max),
    green: CGFloat(arc4random())/CGFloat(UInt32.max),
    blue: CGFloat(arc4random())/CGFloat(UInt32.max),
    alpha: 1.0)
    }

    }



    a


     


    La grille est placée sur la vue maVue, définie sous Storyboard avec des contraintes pour occuper la même place que la SafeArea (pour garantir que cela fonctionne aussi sur un iPhoneX).



     


    Copie d'écran (iPhone 8+) du résultat :


  • Pour obtenir des cases parfaitement rectangulaires, la vue contenant la grille doit toujours avoir un rapport largeur/hauteur de 1,7. Tu peux obtenir ça avec du code, ou une contrainte Storyboard de ratio sur la vue maVue "encapsulant " la grille.


     


    Cela t'aide, jeffadsl ?

  • Une petite variante pour ajouter une marge entre chaque case :



    class Grille:UIView {

    private var _nbLignes = 0
    private var _nbColonnes = 0
    private var _marge:CGFloat = 0

    var couleursCase = [UIColor]()

    init(frame: CGRect, nbLignes:Int, nbColonnes:Int, marge:CGFloat) {
    super.init(frame: frame)
    self._nbLignes = nbLignes
    self._nbColonnes = nbColonnes
    self._marge = marge

    for _ in 0 ..< nbLignes*nbColonnes {
    couleursCase.append(self.couleurAleatoire())
    }
    }

    required init?(coder aDecoder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
    }

    // Dessin de la grille
    override func draw(_ rect: CGRect) {

    let largeur = (rect.width-_marge*CGFloat(_nbColonnes-1))/CGFloat(_nbColonnes)
    let hauteur = (rect.height-_marge*CGFloat(_nbLignes-1))/CGFloat(_nbLignes)

    // Sauvegarde du context graphique initial
    let context = UIGraphicsGetCurrentContext()
    UIGraphicsPushContext(context!)

    // Désactivation anti-aliasing
    // pour avoir des transitions de couleurs nettes
    context?.setShouldAntialias(false)

    var numCase = 0
    for ligne in 0 ..< _nbLignes {
    for colonne in 0 ..< _nbColonnes {
    let rectCase = CGRect(x: CGFloat(colonne)*(_marge+largeur),
    y: CGFloat(ligne)*(_marge+hauteur),
    width: largeur,
    height: hauteur)
    // Lecture couleur case
    let couleur = couleursCase[numCase]
    numCase += 1
    // Dessin case
    couleur.set()
    UIRectFill(rectCase)
    }
    }

    // Restitution du context graphique original
    UIGraphicsPopContext()
    }

    private func couleurAleatoire() -> UIColor {
    return UIColor(red: CGFloat(arc4random())/CGFloat(UInt32.max),
    green: CGFloat(arc4random())/CGFloat(UInt32.max),
    blue: CGFloat(arc4random())/CGFloat(UInt32.max),
    alpha: 1.0)
    }

    }

  • DrakenDraken Membre
    novembre 2017 modifié #22

    Mise en oeuvre :


    a



    import UIKit

    fileprivate let nbColonnes = 10
    fileprivate let nbLignes = 17

    class ViewController: UIViewController {

    @IBOutlet weak var maVue: UIView!

    var grille:Grille?

    override func viewDidLoad() {
    super.viewDidLoad()

    self.view.backgroundColor = UIColor.white
    print ("Taille écran : ", self.view.bounds.size)
    print ("Espace disponible pour les cases : ", maVue.bounds)

    // Création de la view Grille
    let rect = CGRect(x: 0, y: 0,
    width: maVue.bounds.width,
    height: maVue.bounds.height)
    // Grille avec une marge de 2 points
    // entre chaque case
    grille = Grille(frame: rect,
    nbLignes: nbLignes,
    nbColonnes: nbColonnes,
    marge:2.0)
    if let grille = grille {
    maVue.addSubview(grille)
    // Couleur de fond de la grille
    grille.backgroundColor = UIColor.white
    }
    }

    // Exemple de modification d'une case de couleur
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {

    // case aléatoire
    let caseAleatoire = Int(arc4random_uniform(UInt32(nbLignes*nbColonnes)))
    grille?.couleursCase[caseAleatoire] = couleurAleatoire()
    // On informe le système qu'il faut redessiner la grille
    grille?.setNeedsDisplay()
    }

    func couleurAleatoire() -> UIColor {
    return UIColor(red: CGFloat(arc4random())/CGFloat(UInt32.max),
    green: CGFloat(arc4random())/CGFloat(UInt32.max),
    blue: CGFloat(arc4random())/CGFloat(UInt32.max),
    alpha: 1.0)
    }

    }


    a


    Si la marge est fixée à  0, l'affichage est identique à  la version précédente.


     


    Exemple (iPhoneX - marge de 2 points) : 


  • Draken,


     


    C'est juste parfait!!!!


     


    il est vrai que le swift est autrement plus intuitif et léger.


     


    j'essaye d'intégrer mon code pour la réception de donnée ( qui lui est en Objective-c )


     


    je te tiens au courant


     


    10000X merci!!!!


     


    ok4.jpg


  • DrakenDraken Membre
    novembre 2017 modifié #24


     


    il est vrai que le swift est autrement plus intuitif et léger.




     


    Heureusement, vu que le swift a été écrit par des gens avec une grande expérience en Obj-C, dans l'objectif de le remplacer.


     


    Je remarque, dans ton exemple, que la grille est affichée à  partir du haut de l'écran, sous la barre de statut. Est-ce voulu ? 


     


    J'avais eu le même effet à  mon premier essai, que j'ai corrigé en affichant la grille dans une fenêtre située sous la barre de statut, avec une contrainte Storyboard.

  • Juste vous êtes pas en train de réinventer la roue. Une CollectionView aurai été bien plus simple non ?




  • Juste vous êtes pas en train de réinventer la roue. Une CollectionView aurai été bien plus simple non ?




    Cela consomme beaucoup plus de ressources mémoire, de puissance processeur et donc de consommation batterie qu'un drawrect, pour un résultat visuel identique.

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