Note: Sous Xcode 9, il faut choisir un ‘deployment target’ inférieure à iOS 11.0. Au besoin, installer un simulateur roulant la version 10.3 de iOS.
À partir de iOS 11, les réseaux sociaux ne sont plus intégrés à la plate-forme. Il faudra à l’avenir utiliser les frameworks natifs des réseaux sociaux.
Par exemple, pour twitter les instructions sont ici.
Avec ce tutoriel nous apprendrons à programmer une application iOS qui offre à l’utilisateur la possibilité d’habiller sa photo à partir de maquettes de magazines et de pouvoir publier le résultat sur les réseaux sociaux ‘facebook’ et ‘twitter’.
Vidéo du résultat final:
[embedyt]http://www.youtube.com/watch?v=CucHHI7697g[/embedyt]
Note: Le texte en rouge indique une tâche à réaliser en laboratoire.
Action 1 – Créons un nouveau projet de type “Single View” pour le ‘iPad’.
Action 2 – Renseignons les information de création du projet
Action 3 – Ajoutons au projet un ‘groupe’ nommé ‘Mes ressources’.
Action 4 – Ajoutons les trois images précédentes au projet.
Astuce: Il faut enregistrer dans ‘download’ puis, glisser vers le projet.
Action 5 – Plaçons deux contrôles de type ‘UIButton‘ au bas de l’écran et renseignons les propriétés ‘Type‘ et ‘Image‘ pour changer leur apparence.
Action 6 – Glissons ‘ctrl+clic’ le bouton ‘facebook’ vers le le contrôleur de la scène principale.
Action 7 – Renseignons les propriétés du lien du bouton ‘facebook’
Action 8 – Ajoutons le code suivant à la fonction ‘actionFacebook:
@IBAction func actionFacebook(sender: AnyObject) { println("actionFacebook") }
Action 9 – Testons l’app dans le simulateur: Action 10 – Ajoutons une connexion sur le bouton ‘twitter’
// ViewController.swift // TIM-Magazine.swift // 2016.08 - Révision swift 3 import UIKit class ViewController: UIViewController { /// Fonction exécutée suite à un clic du bouton 'facebook' @IBAction func actionFacebook(sender: AnyObject) { print("#actionFacebook\n") } // actionFacebook /// Fonction exécutée suite à un clic du bouton 'twitter' @IBAction func actionTwitter(sender: AnyObject) { print("#actionFacebook\n") } // actionTwitter /// Fonction exécutée suite au chargement de la scène en mémoire override func viewDidLoad() { super.viewDidLoad() } // viewDidLoad } // ViewController
Nous allons maintenant ajouter une scène supplémentaire à notre projet.
Action 11 – Ajoutons un nouveau groupe nommé ‘Les couvertures’ au projet.
Action 12 – Dans le groupe ‘Les couvertures’, ajoutons un nouveau fichier.
Explication en classe de la notion de ‘XIB’ vs ‘StoryBoard’
Action 13 – Sélectionnons un fichier de type ‘iOS – User Interface – View’
Action 14 – Nommons la nouvelle scène
Action 15 – Modifions le fond de la scène et ajoutons un titre.
Note: L’ajustement de l’alpha du fond de la scène nous permettra de voir les éléments sous la scène lorsqu’elle sera ajoutée à la scène principale.
Il nous reste maintenant à charger la scène ‘magazine01’ par programmation.
Idéalement, cette scène devrait être affichée au démarrage de l’application.
Rappel: La scène est une instance de la classe UIViewController.
La classe ‘UIViewController’ contient un membre de type ‘UIView’ qui permet d’afficher des éléments à l’écran.
La classe ‘UIView’ propose la méthode ‘addSubview’ pour ajouter des élements visuels à son canvas d’affichage.
Donc, pour ajouter un élément à la scène principale, il suffit d’appeler la méthode ‘addSubview(un objet visuel)’ du membre ‘view’ de l’instance de la classe de la scène.
Par exemple, pour ajouter un ‘UILabel’ à l’écran il suffit de programmer: view.addSubView(UILabel()).
Note: « self.view » est valide aussi.
Action 16 – Ajoutons le code suivant à la méthode ‘viewDidLoad’ du contrôleur de la scène principale.
override func viewDidLoad() { super.viewDidLoad() // Début de notre code... // Charger le fichier xib let ecranMagazine = UINib(nibName: "Magazine01", bundle: nil).instantiate(withOwner: nil, options: nil)[0] as! UIView // Ajuster la taille de la vue à la taille de la scène ecranMagazine.frame = self.view.bounds; // Ajouter la nouvelle ‘View’ à la scène principale view.addSubview(ecranMagazine); } } //viewDidLoad
Action 17 – Exécutons le projet dans le simulateur
Solution: Insérer la maquette avant les boutons.
Action 18 – Observons la structure des éléments de la scène
Action 19 – Modifions le code, pour ajouter le magazine avant les boutons ‘facebook’ et ‘twitter’
// Insérer la nouvelle ‘View’ avant les boutons fb et twitter print("#view.subviews.count: \(view.subviews.count)\n") view.insertSubview(ecranMagazine, at: (view.subviews.count - 2) - 1 ) // Ou bien ceci pour ajouter au haut de la liste view.insertSubview(ecranMagazine, at: 0)
Note: S’il y a des guides de mise en page, il faut les ajouter au calcul.
Ce qui donne le résultat suivant:
view.insertSubview(ecranMagazine, at: (view.subviews.count - (2 + 2)) - 1 )
Action 20 – Exécutons le projet dans le simulateur
L’approche précédente pose un problème, il faudra modifier l’indice d’insertion à chaque fois que nous ajouterons des éléments de contrôle à la scène principale.
Par exemple, un menu pour changer de magazine.
Il est possible de regrouper des objets visuels ensemble grâce à l’objet ‘UIView’.
Action 21 – Ajoutons un objet ‘UIView’ à la scène principale.
Action 22 – Remplaçons la valeur de paramètre ‘atIndex’ pour ‘view.subviews.count – 1″
// Avec les guides de mise en page view.insertSubview(ecranMagazine, at: view.subviews.count - 3 ) // Sans guides view.insertSubview(ecranMagazine, at: view.subviews.count - 1 ) // Oui bien, insérer la vue au fond de la scène principale: view.insertSubview(ecranMagazine, at: 0)
Action 23 – Renseignons le fond de la scène principale à « noir » et le fond de la maquette ‘Magazine01′ à « Clear color’ (transparent).
Action 24 – Testons avec le simulateur:
Réflexion: Nous assumons que la ‘view’ du fichier XIB sera toujours à la position [0] du XIB. Si Apple change la position de la ‘view’, notre programme ne fonctionnera plus correctemnt.
La méthode recommandée par Apple pour charger la ‘view’ du fichier XIB est la suivante:
//: NOTE, Ne pas utiliser ce code dans votre projet! var ecranMagazine2 = UIView() // Parcourir tous les éléments du fichier for x in UINib(nibName: "magazine01", bundle: nil).instantiate(withOwner: nil, options: nil) { // si objet courant de type 'UIView' alors renseigner notre variable if x.isKind(of:UIView.self) { print("#Nous avons trouvé la View!\n") ecranMagazine2 = x as! UIView } // if } // for
Action 25 – Vous devez réaliser les maquettes suivantes:
Note: Pendant l’étape d’élaboration des maquettes, il est recommandé de fixer la couleur de la scène à noir. Cela va permettre de distinguer le texte blanc. Nous changerons le fond de ‘noir’ à ‘transparent : clearcolor’ à l’étape de chargement des interfaces.
Astuce: Pensez à copier/coller le fichier Magazine01.xib vers 02..04.xib.
Action 26 – Ajoutons un ‘UISegmentedControl’ dans la section interactive.
Résultat final:
Définissons un lien IBAction sur le ‘UISegmentedControl’
/// Fonction exécutée suite à une sélection dans le 'UISegmentedControl'. Note: Doit-être liée au UISC. @IBAction func actionTournerPages(sender: UISegmentedControl) { let selection = sender.selectedSegmentIndex print("#actionTournerPages: la sélection vaut: \(selection)\n") }
@IBAction func actionTournerPages(sender: AnyObject) { // 1 - Retirer le magazine courant de la scène // 2 - Construire une chaine de caractères qui décrit le nom du fichier: 'Magazine0' + selectionCourante + 1 // 3 - Charger le magazine à partir d’un nom d’un fichier NIB // 4 - Ajouter le magazine à la scène principale }
PRÉ-REQUIS – Configuration des comptes sociaux sur l’appareil iOS.
Capture d’une Vue vers un objet UIImage
func sauvegarderEcran(){ // Description : méthode servant à capturer l’écran et au besoin, // en faire une sauvegarde dans l’album photos. // Cacher le menu leMenu.isHidden = true // 1 - Préparer un contexte de dessin à partir de la taille de la scène UIGraphicsBeginImageContext(self.view.bounds.size); // 2 – Dessiner à partir du claque par défaut de la scène self.view.layer.render(in: UIGraphicsGetCurrentContext()!); // 3 – Stocker le résultat dans notre objet local imageFinale = UIGraphicsGetImageFromCurrentImageContext()!; // 5 – Fermer le contexte de dessin UIGraphicsEndImageContext(); // 6 – Facultatif - Stocker une copie de la capture d’écran dans l’album photos // Note: À partir de iOS 10: Info.plist doit contenir une clé 'NSPhotoLibraryUsageDescription' de type 'String' qui indique à l'utilisateur comment sera utilisé sa librairie de photos. UIImageWriteToSavedPhotosAlbum(imageFinale, nil, nil, nil ); leMenu.isHidden = false } // sauvegarderEcran
Action – Ajouter le code suivant dans la méthode posterSurFacebook:
sauvegarderEcran()
Action – Tester l’application:
Note: Sous iOS 10, il devrait y avoir l’erreur suivante:
TIM.Magazine[16985:6613925] [access] This app has crashed because it attempted to access privacy-sensitive data without a usage description. The app’s Info.plist must contain an NSPhotoLibraryUsageDescription key with a string value explaining to the user how the app uses this data.
Action: Ajouter au fichier ‘Info.plist’ la clé ‘NSPhotoLibraryUsageDescription’:
Attention: À partir de Xcode 9 il faut plutôt ajouter la clé ‘NSPhotoLibraryAddUsageDescription’
Ou bien en éditant le fichier info.plist et en ajoutant le code suivant:
<key>NSPhotoLibraryUsageDescription</key> <string>Puis-je utiliser votre album photo?</string>
Action – Tester l’application:
Note: Sous iOS 10, il ne devrait plus y avoir d’erreur:
Action: Vérifions la présence de la capture dans l’album de photos du simulateur. (Maj+CMD+h)
import Social func posterSurFacebook() { // Les étapes pour utiliser ‘facebook’ // 1 - Tester si le service et les informations de connexion sont dispo if SLComposeViewController.isAvailable(forServiceType: SLServiceTypeFacebook) { // 2 - Créer un feuille pour le 'post' if let controleur = SLComposeViewController (forServiceType: SLServiceTypeFacebook) { // 3 - Composer le message controleur.setInitialText("Test avec Swift 3 et Xcode 8"); self.present(controleur, animated: true, completion: nil); // 4 - Ajouter une image - facultatif controleur.add(imageFinale); // 5 - Ajouter un lien - facultatif controleur.add(NSURL(fileURLWithPath: "http://tim.cstj.qc.ca") as URL!); // 6 - Présenter la fenêtre de confirmation à l'utilisateur // self.present(controleur, animated: true, completion: nil); // Ne fonctionne plus pour Facebook sous Xcode 8B5. Par contre, fonctionne avec twitter. // Temporairement remplacer par: self.navigationController?.pushViewController(controleur, animated: true) } } // if Facebook } // posterSurFacebook
Action – Modifier la méthode ‘actionFacebook’.
@IBAction func actionFacebook(sender: AnyObject) { print("actionFacebook") sauvegarderEcran() posterSurFacebook() } // actionFacebook
Action – Tester l’application
Attention – Assurez-vous de tester avec un simulateur sous iOS 10. Les choses se sont sérieusement compliquées sous iOS 11.
Action – Modifier le méthode actionTwitter
Il reste à cacher la zone menu avant de capturer l’écran.
Définissons un IBOutlet sur la View du menu.
Ajoutons le code suivant à la méthode capturerEcran.
func sauvegarderEcran(){ // Cacher le menu leMenu.isHidden = true // 1 ... // 6 leMenu.isHidden = false } // sauvegarderEcran
Note: Cette étape est facultative. Il n’y aura pas d’explication en classe, Si vous la réalisez, la correction du projet se fera sur 12/10. Si vous optez pour cette option, il faudra le préciser dans votre archive de remise.
Le contrôle ‘UIScrollView’ permet d’afficher un contenu plus grand que sa zone d’affichage.
Une gestuelle sur l’écran donne accès au contenu non visible.
Méthode d’utilisation du contrôle:
1 – Placer le ‘UIScrollView’ sur une scène,
2 – Par programmation, ajouter des objets dans le ‘UIScrollView’ en renseignant correctement les positions x/y,
3 – Renseigner la propriété ‘UIScrollView’.contentSize
func chargerLesMagazines() { // Renseigner la largeur de la zone de contenu du scrollView en fn du nb de pages. let pagesScrollViewSize = zoneAffichageDesElements.frame.size zoneAffichageDesElements.contentSize = CGSizeMake(pagesScrollViewSize.width * Float(nbMagazines), pagesScrollViewSize.height) for page in 0..<nbMagazines { // Placer les Views un à la suite de l'autre sur le plan des 'x' var frame:CGRect = zoneAffichageDesElements.bounds frame.origin.x = frame.size.width * Float(page) // la première page (0) sera à x = 0 frame.origin.y = 0 // Charger le 'xib' courant var nomNIB = "Magazine0\(page+1)" println(nomNIB) if var newPageView = UINib(nibName: nomNIB, bundle: nil).instantiateWithOwner(nil, options: nil)[0] as? UIView { newPageView.frame = frame; // Ajouter au scrollView zoneAffichageDesElements.addSubview(newPageView) } // if newPageView } // for page } // chargerLesMagazines()
Testons
Activons le ‘paging enable’
Ajoutons un UIPageControl (indicateur de la page courante/nbPages)
Expliquer Délégation
// Une des Méthodes de délégation de UIScrollView func scrollViewDidEndDecelerating(scrollView: UIScrollView){ println("scrollViewDidEndDecelerating") let positionPage = Int(zoneAffichageDesElements.contentOffset.x / zoneAffichageDesElements.frame.size.width) indicateurDePages.currentPage = positionPage } // scrollViewDidEndDecelerating
Remplaçons le code de la méthode ‘viewDidLoad.
override func viewDidLoad() { super.viewDidLoad() // Début de notre code... // L'ancien code de chargement du magazine01 doit-être supprimé. chargerLesMagazines() } // viewDidLoad
Un exemple de scrollView
FIN du laboratoire