NSURLSession ordonnée

macphimacphi Membre
février 2016 modifié dans API UIKit #1

Bonjour à  tous


 


Je suis un (presque) débutant en programmation iOS.


Pour m'entraà®ner et apprendre, je développe des micro-apps.


 


Et là  je bute sur problème.


 


Je voudrais, sans librairie externe, avec NSURLSession, animer un UIImageView à  partir de 10 images téléchargées sur un site internet.


Je me sers de NSURLSession sans trop de souci mais pas pour ce cas-là .


 


Le code est simple mais malheureusement NSURLSession ne télécharge pas les images dans l'ordre du coup l'animation ne fonctionne pas.


En bidouillant avec des semaphores et autres NSTimer j'y arrive mais je sais que ce n'est pas propre.


 


Pourriez-vous m'aider à  avancer sur le sujet ?


 


Merci


 


Voici le code en question :




    func imgTest() {
        
        for urlPath in urlPaths {
       
        let url:NSURL = NSURL(string: urlPath)!
        
        let session = NSURLSession.sharedSession()
            
        let request = NSMutableURLRequest(URL: url)


       let task = session.dataTaskWithRequest(request) {
            (let data, let response, let error) in


            guard let _:NSData = data, let _:NSURLResponse = response  where error == nil else { print("error")
                return
            }
            
            dispatch_async(dispatch_get_main_queue(), {
                let dataImg = UIImage(data: data!)
                self.imagesListArray.append(dataImg!)
                self.img_Radar.animationImages = self.imagesListArray
                self.img_Radar.animationDuration = 3.0
                self.img_Radar.animationRepeatCount = 10
                self.img_Radar.startAnimating()
            })            
        }
        task.resume()   
    }
}

Réponses

  • DrakenDraken Membre
    février 2016 modifié #2

    Pourquoi est-ce que tu n'attends pas d'avoir récupéré les images en mémoire, avant de lancer l'animation ?


     


    Tu peux aussi utiliser le balises de code du forum (symbole <> à  coté de la bulle de bande dessinée) pour afficher le source d'une manière plus lisible.

  • Oui je n'aurais pas dû laisser le code pour l'animation.


     


    Mais dans tous les cas que j'ai essayé le tableau n'est pas dans l'ordre.


    Et les images au format binaire ne sont plus triables (enfin je pense)


    Et c'est ça qui m'échappe...

  • CéroceCéroce Membre, Modérateur
    février 2016 modifié #4
    Quand on lance des requêtes sur internet, on n'est jamais sûr de l'ordre dans lequel on recevra les données. D'une part, tes images n'ont pas toutes la même taille, donc certaines se chargeront plus vite. D'autre part, les paquets IP peuvent prendre des chemins différents sur le réseau.

    Il faut que tu télécharges toutes les images. Les images ne sont effectivement pas triables, mais étant donné que c'est toi qui a lancé les requêtes, tu sais à  quelle requête correspond chaque réponse et tu peux donc retrouver l'ordre: l'index de l'image est le même que celui de la requête.

    C'est faisable, mais pas si simple, surtout quand on débute.
  • AliGatorAliGator Membre, Modérateur
    février 2016 modifié #5
    Bah en même temps c'est un peu normal, rien ne garantit si tu envoie 5 requêtes en même temps qu'elles vont arriver dans le même ordre que celui dans lequel tu les as envoyées. Certaines requêtes réseau peuvent prendre plus de temps que d'autres. Donc c'est à  toi de les re-trier.

    Le mieux est sans doute, quand tu reçois une image (donc dans ton bloc de completion de ta dataTaskWithRequest), de la réassocier à  l'URL d'origine (dans un Dictionary par exemple, qui aurait pour clé l'URL et comme valeur l'image). Et une fois que toutes les images sont téléchargées, et donc que ton dictionnaire URL -> Image est rempli, tu tries les images en utilisant les URLs pour déterminer l'ordre.

    Ca donnerait un truc comme ça :

    func imgTest() {
    var images: [String: UIImage?]
    for urlPath in urlPaths {

    let url:NSURL = NSURL(string: urlPath)!
    let session = NSURLSession.sharedSession()
    let request = NSMutableURLRequest(URL: url)

    let task = session.dataTaskWithRequest(request) {
    (let data, let response, let error) in
    guard let realData:NSData = data where error == nil else {
    return print("error")
    }

    let dataImg = UIImage(data: realData)
    images[urlPath] = dataImg
    if images.count == urlPaths.count {
    // Si toutes les images sont arrivées
    dispatch_async(dispatch_get_main_queue()) {
    self.imagesListArray = urlPaths.flatMap { images[$0] }.flatMap { $0 }
    self.img_Radar.animationImages = self.imagesListArray
    self.img_Radar.animationDuration = 3.0
    self.img_Radar.animationRepeatCount = 10
    self.img_Radar.startAnimating()
    }
    }
    }
    task.resume()
    }
    }
    L'idée est :

    1) De ne mettre à  jour ton tableau imagesListArray et commencer l'animation que quand toutes les requêtes de tes images sont toutes finies (quel que soit l'ordre dans lequel elles sont arrivées)

    2) De construire le tableau d'images self.imagesListArray à  partir de urlPaths (qui est dans un tableau déjà  l'ordre que tu veux avoir) et de images (qui est un tableau faisant correspondre chaque urlPath à  l'image associée une fois qu'elle est arrivée), en utilisant flatMap pour convertir ton tableau de String en tableau de UIImage

    Le 2ème flatMap est nécessaire parce que les valeurs du dictionnaire "images" étant de type "UIImage?", alors "images[key]" retourne un "UIImage??" donc "flatMap { images[$0] }" va enlever un niveau d'Optional, mais il faut en enlever un 2ème avec un "flatMap { $0 }" pour avoir un tableau de "UIImage" et non de "UIImage?"
  • FKDEVFKDEV Membre
    février 2016 modifié #6
    Ali, n'y a t-il pas un problème d'accès multitâche au dictionnaire "images" ?
     

    Une autre possibilité que le Dictionnaire consisterait à  créer une petite classe pour lier l'url et l'image téléchargée.
     
    class RemoteImage
    {
    var url:String
    var image:UIImage?
    init(url:String)
    {
    self.url = url;
    }
    }
     
    Donc, plutôt que d'avoir un tableau d'url d'un côté et un tableau d'image de l'autre, tu aurais un tableau de RemoteImage.
    Tu peux mettre ton appel à  NSURLSession dans une fonction et capturer l'objet RemoteImage en cours dans la closure de completion de dataTaskWithRequest:
     
    func DownloadImage(remoteImage:RemoteImage)
    {
    guard let url:NSURL = NSURL(string: remoteImage.url)
    else {print("error");return }

    let session = NSURLSession.sharedSession()
    let request = NSMutableURLRequest(URL: url)

    dispatch_group_enter(self.group)
    let task = session.dataTaskWithRequest(request) {
    (let data, let response, let error) in

    defer { dispatch_group_leave(self.group) } //sera fait de toutes façons
    guard let realData:NSData = data where error == nil else {
    return print("error")
    }

    remoteImage.image = UIImage(data: realData)
    }
    task.resume()
    }
    }
    Tu peux utiliser un dispatch group pour exécuter un block quand toutes les images sont prêtes:
     
    self.group = dispatch_group_create()

    dispatch_group_enter(self.group)

    //Ce block sera exécuté quand le nombre de dispatch_group_leave aura atteint le nombre
    //de dispatch_group_enter.
    dispatch_group_notify(self.group, dispatch_get_main_queue(),
    {
    self.img_Radar.animationImages = self.remoteImages.flatMap { $0.image }
    self.img_Radar.animationDuration = 3.0
    self.img_Radar.animationRepeatCount = 10
    self.img_Radar.startAnimating()
    });


    for (var remoteImage in self.remoteImages)
    {
    self.downloadImages(remoteImage)
    }
    dispatch_group_leave(self.group)

    C'est écrit sans compilation, donc il y a surement des erreurs, mais l'idée est là .
  • Quand je pense que je croyais que c'était un exercice simple...!


    ::)


     


    Merci à  tous pour vos réponses très détaillées et pour le code que je vais m'empresser de décortiquer/tester dès ce soir.


     


    Le flatMap, ça ne me dit rien, juste le .map dans le livre Swift d'Apple.


    Quand au dispatch_group, là  aussi il faut que je me rencarde.


     


     


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