Comment asynchrone charger une image dans une UIImageView?
J'ai une UIView avec une UIImageView sous-vue. J'ai besoin de charger une image dans le UIImageView sans blocage de l'INTERFACE utilisateur. L'appel de blocage semble être: UIImage imageNamed:
. Voici ce que j'ai pensé a résolu ce problème:
-(void)updateImageViewContent {
dispatch_async(
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
UIImage * img = [UIImage imageNamed:@"background.jpg"];
dispatch_sync(dispatch_get_main_queue(), ^{
[[self imageView] setImage:img];
});
});
}
L'image est de petite taille (150x100).
Cependant l'INTERFACE est toujours bloqué lors du chargement de l'image. Ce qui me manque ?
Voici un petit exemple de code qui présente ce comportement:
Créer une nouvelle classe basée sur UIImageView, définir son interaction avec l'utilisateur pour OUI, ajoutez deux instances dans une UIView, et de mettre en œuvre ses touchesBegan méthode comme ceci:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
if (self.tag == 1) {
self.backgroundColor= [UIColor redColor];
}
else {
dispatch_async(dispatch_get_main_queue(), ^{
[self setImage:[UIImage imageNamed:@"woodenTile.jpg"]];
});
[UIView animateWithDuration:0.25 animations:
^(){[self setFrame:CGRectInset(self.frame, 50, 50)];}];
}
}
Attribuer le point 1 de l'un de ces imageViews.
Ce qui se passe exactement lorsque vous appuyez sur les deux points de vue, presque simultanément, à commencer par la vue qui charge une image? L'INTERFACE utilisateur bloqué parce qu'il est en attente pour [self setImage:[UIImage imageNamed:@"woodenTile.jpg"]];
de retour ? Si oui, comment dois-je faire cela de façon asynchrone ?
Ici est un projet sur github avec ipmcc code
Utiliser un long appuyez sur, puis faites glisser pour dessiner un rectangle autour de la carrés noirs. Ce que je comprends sa réponse, en théorie, le blanc rectangle de sélection ne doit pas être bloqué la première fois que l'image est chargée, mais elle ne l'est réellement.
Deux images sont incluses dans le projet (un petit: woodenTile.jpg et une plus grande: bois.jpg). Le résultat est le même avec les deux.
Format d'Image
Je ne comprends pas vraiment comment cela est lié au problème que je reste avec l'INTERFACE utilisateur bloqué alors que l'image est chargée pour la première fois, mais les images PNG décoder sans blocage de l'INTERFACE utilisateur, tandis que des images JPG ne bloquer l'INTERFACE utilisateur.
Chronologie des événements
Le blocage de l'INTERFACE utilisateur commence ici..
.. et se termine ici.
AFNetworking solution
NSURL * url = [ [NSBundle mainBundle]URLForResource:@"bois" withExtension:@"jpg"];
NSURLRequest * request = [NSURLRequest requestWithURL:url];
[self.imageView setImageWithURLRequest:request
placeholderImage:[UIImage imageNamed:@"placeholder.png"]
success:^(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image) {
NSLog(@"success: %@", NSStringFromCGSize([image size]));
} failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error) {
NSLog(@"failure: %@", response);
}];
//this code works. Used to test that url is valid. But it's blocking the UI as expected.
if (false)
if (url) {
[self.imageView setImage: [UIImage imageWithData:[NSData dataWithContentsOfURL:url]]]; }
La plupart du temps, il se connecte: success: {512, 512}
Il a aussi parfois des journaux: success: {0, 0}
Et parfois: failure: <NSURLResponse: 0x146c0000> { URL: file:///var/mobile/Appl...
Mais l'image n'est jamais changé.
- Il vous manque rien. Changement de l'INTERFACE ont toujours synchrone. C'est le meilleur que vous pouvez faire.
- Quelle est la taille de l'image? Vous trouverez probablement que l'appel de blocage n'est pas le chargement de l'image, mais plutôt à déchirer le contenu de l'image lors de l'affichage de l'image est affichée sur l'écran. Avez-vous essayé de profilage de l'application pour voir où le col de la bouteille est que vous simplement aveuglément en supposant que le chargement de l'image est le problème?
- J'ai d'abord été chargement de l'image dans une méthode de classe, une seule fois par toutes les instances de la classe. Le blocage ne se produit que la première fois, ce qui me conduisent à penser qu'il a été causé par UIImage imageNamed.
- Utiliser des instruments pour trouver le col de la bouteille.
- Voir ma question et de la réponse de here.
- C'est intéressant .La accepté de répondre ressemble à ce que ipmcc proposé ici. Mais il y a des différences.
Vous devez vous connecter pour publier un commentaire.
Le problème est que
UIImage
ne fait pas les lire et décoder l'image jusqu'à ce que la première fois qu'il est effectivement utilisé/dessiné. À force de ce travail pour arriver sur un thread d'arrière-plan, vous devez utiliser/dessiner l'image sur le thread d'arrière-plan avant de faire le thread principal-setImage:
. Cela a fonctionné pour moi:EDIT: L'INTERFACE utilisateur n'est pas bloquant. Vous avez spécifiquement configuré pour utiliser
UILongPressGestureRecognizer
qui attend, par défaut, une demi-seconde avant de faire quoi que ce soit. Le thread principal est toujours le traitement des événements, mais rien ne se passe jusqu'à ce que le GR du temps. Si vous faites cela:...vous remarquerez qu'il obtient beaucoup plus vif. Le chargement de l'image n'est pas le problème ici.
EDIT 2: j'ai regardé le code, comme affiché sur github, en cours d'exécution sur un iPad 2, et je n'ai tout simplement pas obtenir le moindre problème que vous décrivez. En fait, il est tout à fait lisse. Voici une capture d'écran de l'exécution du code dans le CoreAnimation instrument:
Comme vous pouvez le voir sur le graphique du haut, le FPS va jusqu'à ~60 im /s et y reste tout au long de la geste. Sur le graphique du bas, vous pouvez voir l'écho à propos de 16 ans qui est l'endroit où l'image est chargée pour la première fois, mais vous pouvez voir qu'il n'y a pas une baisse dans le taux de trame. Juste de l'inspection visuelle, je vois la sélection de la couche se croisent, et il y a un petit, mais observables délai entre la première intersection et l'apparence de l'image. Aussi loin que je peux dire, le fond du chargement de code est en train de faire son travail comme prévu.
Je souhaite que je pourrais vous aider plus, mais je ne suis pas à voir le problème.
UILongPressGestureRecognizer
non pas à cause du chargement de l'image.selectionLayer
arrêter de redessiner et la suite de mon doigt la première fois une image doivent être chargés, mais vous devez exécuter le code sur un iPad (mini dans mon cas) pour voir ce.Vous pouvez utiliser AFNetworking bibliothèque , dans laquelle par l'importation de la catégorie
"UIImageView+AFNetworking.m"
et à l'aide de la méthode comme suit :
espère que cette aide .
J'ai eu un problème similaire avec ma demande où j'ai eu à télécharger beaucoup d'images et avec ce que mon INTERFACE est continuellement mise à jour. Ci-dessous le tutoriel simple lien qui a résolu mon problème:
NSOperations & NSOperationQueues Tutoriel
c'est la bonne façon:
Pourquoi n'utilisez-vous pas la troisième partie de la bibliothèque comme AsyncImageView? Avec cela, tout ce que vous avez à faire est de déclarer votre AsyncImageView objet et passer l'url ou l'image que vous souhaitez charger. Un indicateur d'activité s'affiche pendant le chargement de l'image et rien ne va bloquer l'INTERFACE utilisateur.
-(void)touchesBegan: est appelé dans le thread principal. En appelant dispatch_async(dispatch_get_main_queue) il vous suffit de mettre le bloc dans la file d'attente. Ce bloc sera traité par PGCD lorsque la file d'attente sera prêt (c'est à dire le système est plus du traitement de vos touches). C'est pourquoi vous ne pouvez pas voir votre woodenTile chargé et attribué à soi-même.l'image jusqu'à ce que vous relâchez votre doigt et laissez le PGCD processus de tous les blocs qui ont été mis en file d'attente dans la file d'attente principale.
Remplacement :
par :
devrait résoudre votre problème... au moins pour le code qui présente à elle.
Envisager d'utiliser SDWebImage: il ne télécharge pas seulement et des caches de l'image en arrière-plan, mais aussi les charges et les rend à la fois.
Je l'ai utilisé avec de bons résultats dans un tableview qui ont de grandes images qui ont été lent à charger même après avoir téléchargé.
https://github.com/nicklockwood/FXImageView
C'est une image qui peut gérer arrière-plan de chargement.
Utilisation
Pour obtenir une URL de votre fichier, vous pouvez trouver les suivantes:
L'obtention de faisceau de références de fichier /chemins d'accès à l'application se lance
Lorsque vous utilisez AFNetwork dans une application, vous n'avez pas besoin d'utiliser un bloc de chargement de l'image parce que AFNetwork offre une solution pour elle. Comme ci-dessous:
Et
Grâce
Une façon que j'ai mis en œuvre est le Suivant: (Bien que je ne sais pas si c'est le meilleur)
Au début, j'ai créer une file d'attente en utilisant la Série Asynchrone ou sur la File Parallèle
**
Qui jamais vous peut trouver mieux à vos besoins.
Après: