• Billet écrit dans : iPhone 14.02.2010 Aucune réponse à cet article.

    Bonjour, je recommence à développer sur iphone après quelques mois d’arrêt, j’en profite aussi pour tester l’Ipad (sur simulateur :p).

    Cette semaine j’ai cherché à faire fonctionner une UISearchBar avec le controller qui va bien et j’ai eu quelque souci. Si techniquement c’est assez simple à mettre en place, graphiquement j’ai eu quelque souci.

    Mon architecture :

    Mon application a une UITabBar, puis sur une des sous vue j’ai une liste de résultat avec la SearchBar. Cet écran n’est pas  directement une UITableView, c’est un navigation controller qui contient une UITableView.

    Mon archi

    Ma UITableView est crée directement en code et est à hauteur fixe, et mes cellules ont une hauteur de 60:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    - (void)viewDidLoad {  
    	CGRect frame = CGRectMake(0, 75, 320, 262);
    	// Initialise une table view.
    	myTableView = [[UITableView alloc] initWithFrame:frame];
    	myTableView.rowHeight = 60;
     
    	// Ajout la tableView à l'écran et autres par la suite
     
    }

    Mes soucis

    Les soucis viennent quand j’utilise la barre de recherche:

    • Les cellules sont bien réutilisées mais la hauteur est celle par défaut du coup tout mes items sont mal placés. Joli bug graphique.
    • Mes résultats sont bien filtrés, mais il est impossible de scroller. La liste revient toujours en haut. Comme si ma liste avait une hauteur tellement grande que le scroll était inefficace.

    Solutions

    Bon, j’en suis pas vraiment fier, ça tient plus de hooks qu’autres choses mais ça a le mérite de marcher et ça n’a pas l’air trop lourd en terme de performance sur mon 3GS.

    En fait c’est simple, à chaque fois il faut refixer les hauteurs au moment oppertun.

    • Pour les cellules, c’est à leur initialisation :
      1
      2
      3
      4
      
      - (CGFloat) tableView: (UITableView *) tableView heightForRowAtIndexPath: (NSIndexPath *) indexPath
      {
      	return 60;
      }
    • Pour la liste, c’est quand on lance une recherche :

      1
      2
      3
      4
      5
      6
      7
      
      - (void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope
      {
      	CGRect frame = CGRectMake(0, 75, 320, 262);
      	[[[self searchDisplayController] searchResultsTableView] setFrame:frame];
       
      	// faire sa recherche ensuite
      }

    Donc en terme de performance y doit y avoir moyen de fixer ses tailles une fois pour toute mais je n’ai pas trouvé comment. Si quelqu’un a la solution je suis preneur.

    Billet écrit dans : iPhone 14.02.2010 Aucune réponse à cet article.
  • Billet écrit dans : Découverte PHP, Symfony 03.10.2009 4 Réactions !!

    J’ai (re)découvert un truc vraiment sympathique au boulot c’est le système de behavior doctrine intégré à symfony.

    Hein ? Mais à quoi ça sert ?

    Ce que j’en retiens c’est que cela peut permettre d’automatiser certaines actions (répétitives) à l’enregistrement en base, et donc d’enrichir son modèle et deux coup de cuillère à pot.

    Prenons un exemple, vous devez faire un site basé sur la créations de contenus par vos utilisateurs. Ils peuvent écrire des billets, commenter, s’envoyer des messages privés, gérer un annuaire .. bref un site où l’on doit quasiment tout relié à un utilisateur. Chaques tables auraient donc au moins un « user_id » comme clée étrangère ce qui implique dans vos actions d’associer l’utilisateur courant à l’objet pour le sauvegarder .. et ce pour chaque table … lourd non ?

    Hé bien grâce aux behaviors vous pouvez sortir ce comportement générique dans des classes et avec seulement de la configuration au niveau de votre schéma, vous pouvez associer ce comportement à n’importe quel table. L’exemple que j’ai pris est tiré du behavior « Signable » du plugin sfDoctrineActAsSignablePlugin.

    Configuration

    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    Item:
      actAs:
        Signable:
      columns:
        id:
          type:             integer
          primary:          true
          autoincrement:    true
        champ1:             string(255

    Comment ça marche ?

    Donc si on résume, il faut dire à Doctrineque l’on va rajouter des colonnes .. et que l’on veut être prévénu lors de l’insertion en base pour associé nos actions. Donc avec seulement 2 classes, une de définition et une d’écoute, on peut s’en sortir et crée notre behavior.

    Lors de la configuration, en rajoutant « actAs : Signable », Doctrine va chercher une classe de définition qui ira  étendre une classe Doctrine_Template. Par convention il ira chercher une classe nommé Doctrine_Template_Signable. Le rôle de cette classe est de déclarer ses colonnes et de rajouter une écoute, un listener, vers l’autre classe d’actions.

    Pour mon exemple, je vais volontairement raccourcir les classes du sfDoctrineActAsSignablePlugin et prendre pour acquis que vos utilisateurs sont gérés via sfGuardDoctrinePlugin.

    Doctrine_Template

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    
    class Doctrine_Template_Signable extends Doctrine_Template {
     
      // Définitions des relations
      public function setUp() {
      // Bim rajoute une clée étrangère sur sfGuard
      $this->hasOne('sfGuardUser as Author', array(
                'local' => 'created_by',
                'foreign' => 'id'
                )
                );
      }
     
     public function setTableDefinition() {
     
      // Bim une colonne en plus
      $this->hasColumn('created_by', 'integer', 4, array(
              'type' => 'integer',
              'length' => '4',
         ));
     
      // Lien avec notre 2eme classe .. on passe le nom de la colonnette rajouté
      $this->addListener(new Doctrine_Template_Listener_Signable('created_by));
      }
    }

    Là pour l’exemple aucune configuration au niveau du schéma n’est possible mais c’est facile à rajouter via d’un tableau d’options et le constructeur suivant :

    1
    2
    3
    
      public function __construct(array $options = array()) {
        $this->_options = Doctrine_Lib::arrayDeepMerge($this->_options, $options);
      }

    Doctrine_Template_Listener

    Nous avons vu que notre Template rajoute un listener vers notre 2e classe en lui passant la colonne à surveiller (dans la vrai vie, un tableau d’options ..). Le rôle de notre listener de pouvoir agir avant/après l’insertion/édition d’un objet .. facile :

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    
    class Doctrine_Template_Listener_Signable extends Doctrine_Record_Listener {
     
      protected $_colonette = "created_by";
     
      public function __construct($colonne) {
        $this->_colonette = $colonne;
      }
     
      public function preInsert(Doctrine_Event $event) {
          $createdName = $this->_colonette;
     
          // recupère l'objet appellant :
          $objet = $event->getInvoker();
     
          // Affectation de valeur
           $objet->$createdName = $this->getUserId();
      }
     
      public function getUserId() {
         // Echappe le mode cli
         if (0 != strncasecmp(PHP_SAPI, 'cli', 3)) {
           // L'user courant
           return sfContext::getInstance()->getUser()->getAttribute('user_id', null, 'sfGuardSecurityUser');
         } else {
          return null;
         }
      }
     
      public function preUpdate(Doctrine_Event $event) {
      }
     
      public function postUpdate(Doctrine_Event $event) {
      }
     
      public function postInsert(Doctrine_Event $event) {
      }
     
      // A tester : public function Delete(Doctrine_Event $event) ..
    }

    Vous n’avez plus qu’a rebuilder votre model et magie ça doit marcher !!

    Vu que l’on a fait une relation de type One-to-many sur sfGuard vous pouvez accéder à l’utilisateur sfGuard via la propriété Author sur n’importr quel table marqué comme « Signable »

    1
    
      echo $objet->Author->id;

    Voilà .. simple au final non ? Ah vous de créez le votre, :p

    Comme on dirait au boulot « Amaaziiiing !! »

    Billet écrit dans : Découverte PHP, Symfony 03.10.2009 4 Réactions !!
  • Billet écrit dans : Mobile, iPhone 23.07.2009 5 Réactions !!

    Salut,
    Il m’est arrivé plusieurs fois qu’on me demande ce qu’il faut pour bien débuter sur iPhone. La question est simple, la réponse un peu moins. Je vais commencer par le plus simple, le matériel.

    Matériel :

    • un Mac intel. Désolé mais vos vieux mac à la cave ne marcheront pas.
    • un iPhone de test. Le simulateur ne suffit pas.
    • une licence de développeur Apple. (Pas bien cher, 70€ je crois ..). Ceux qui disent  « Mais moi je jailbreak j’envoie mes appli en SSH et fuck Apple » je leur répond « Vous vous faites bien chier. » Et les solutions pour auto signer son code pour l’envoyer « comme en vrai » c’est pas trop la joie (faut refaire les manips pour chaque projets ..)

    Sinon pour développer sur PC (windows notamment) sachez qu’on peut compiler des applis et se les envoyer en SSH etc. C’est faisable, mais alors quel perte de temps et vous verrez dans la suite de l’article qu’on y gagne pas tant que ça.

    Logiciels :

    • Xcode le seul et unique IDE sympathique. Il auto-complète un peu, permet de compiler directement sur le téléphone sans prise de tête, fourni un debugger pas à pas.
    • Interface Builder. Ça c’est le truc tape à l’oeil  qu’Apple met en avant. C’est pour construire les écrans et relier les éléments graphiques à leurs représentations « code ». C’est super pratique et beaucoup plus ergonomique que de le faire sur Android :
      // Lie les champs graphiques à des champs codes
      EditText login = (EditText) findViewById(R.id.login);
    • Instruments. Super pratique pour voir l’état en temps réel de l’iphone : consommation CPU, RAM, allocations d’objets etc. C’est avec ce genre d’outils que tu te dis « en fait je ne sais pas coder léger »

    Donc on voit bien que même s’il existe des solutions pour compiler sur PC, les « toolchain iphone » c’est quand même dommage de se séparer de ces logiciels. Encore que pour Xcode eclipse doit pouvoir suffir, les deux autres logiciels n’ont pas l’air d’avoir d’équivalent.

    Et puis la finalité de développer sur iPhone c’est bien d’avoir son quart d’heure de gloire sur l’appStore non ? Alors autant commencer bien et dans la légalité. Car si Apple découvre que vous n’avez pas utilisé ses outils, que votre appli est dispo en jailbreak etc .. ben c’est foutu après.

    Langage de programmation

    Je suis passé par 3 phases quand j’ai commencé à programmer en objective-c.

    • Déroutant au début (retain une variable ? Kesako ?)
    • a l’air d’avoir des mécanismes complexes (le parsage d’XML est chelou quand on vient du web php/as3 etc)
    • Pratique et puissant quand on comprend les mécanismes par delegate, que l’on joue avec les threads, que l’on crée ses propres classes outils (comme pour ma classe de requete http)

    Mais comment apprendre ce langage ? la réponse est simple : dans la documentation.

    Documentation et Tutoriel

    La documentation et les guides Apple sont bien pratiques pour appréhender le langage et les mécanismes.

    Cependant pour appréhender Interface Builder et sa relation avec le code c’est pas trop ça. Je conseille les tutos d‘ipup.fr qui ont pas mal d’images et surtout que c’est français.

    Sinon pour plus de liens, je vous conseille toujours les liens de mon précédent article « créer des applications iphone » et mon flux delicious :

    ps : Si vous voulez suivre mes « découvertes » iphone,  mon flux delicious est constamment mis à jour ! http://delicious.com/onishinji/iphone

    Billet écrit dans : Mobile, iPhone 23.07.2009 5 Réactions !!
CV Guillaume chave