2012-07-12 9 views
5

Cuando uso ALAssetsLibrary para obtener fotos locales, funciona bien. Pero después de eliminar algunas fotos con la aplicación 'Fotos', mi aplicación falla.ALAssetsLibrary parece devolver un número incorrecto de mis fotos

Crash información es:

"Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[NSOrderedSet enumerateObjectsAtIndexes:options:usingBlock:]: index 14 beyond bounds [0 .. 9]'".'14'

Parece que el número de fotos locales sigue siendo el mismo que befoore. E incluso después de salir de mi aplicación y reiniciarla de nuevo, aún falla.

local código de acceso de la foto:

dispatch_async(dispatch_get_main_queue(),^
{ 
    @autoreleasepool 
    { 
     ALAssetsLibraryAccessFailureBlock failureblock = ^(NSError *myerror) 
     { 
      NSLog(@"error occour =%@", [myerror localizedDescription]); 
     }; 

     ALAssetsGroupEnumerationResultsBlock groupEnumerAtion = ^(ALAsset *result, NSUInteger index, BOOL *stop) 
     { 
      if (result!=NULL) 
      { 
       if ([[result valueForProperty:ALAssetPropertyType] isEqualToString:ALAssetTypePhoto]) 
       { 
        [self.g_imageArray addObject:result]; 
       } 
      } 
     }; 

     ALAssetsLibraryGroupsEnumerationResultsBlock 
     libraryGroupsEnumeration = ^(ALAssetsGroup* group, BOOL* stop) 
     { 
      if (group == nil) 
      { 
       return; 
      } 

      if (group!=nil) { 
       [group enumerateAssetsUsingBlock:groupEnumerAtion]; 
      } 
     [self updatephotoList]; 
     }; 

     self.library = [[ALAssetsLibrary alloc] init]; 
     [self.library enumerateGroupsWithTypes:ALAssetsGroupSavedPhotos 
           usingBlock:libraryGroupsEnumeration 
          failureBlock:failureblock]; 
    } 
}); 

Si tomo otra foto con la cámara del sistema, mi solicitud hace de nuevo en Aceptar.

+0

Una pregunta: ¿está seguro de que '[auto updatephotoList]' se llamará _after_ el último bloque enumeración de '[grupo enumerateAssetsUsingBlock ... ] '? Tengo docenas de bloqueos en mi aplicación, así que verificará este enfoque y veré cómo funciona: http://stackoverflow.com/a/23378441/649379 – SoftDesigner

Respuesta

2

Esto parece ser un error de iOS, como usted dijo ALAssetsLibrary devolvió el número incorrecto de sus fotos por lo que obtuvo el índice de error fuera de límites. La solución consiste en recargar tu foto de nuevo como éstos:

ALAssetsLibraryGroupsEnumerationResultsBlock 
libraryGroupsEnumeration = ^(ALAssetsGroup* group, BOOL* stop) 
{ 
     if (group == nil) 
     { 
      return; 
     } 
     //Force to reload photo as numberOfAssets is broken 
     NSLog(@"how many picture I have in this group: %d",[group numberOfAssets]); 
     [group setAssetsFilter:[ALAssetsFilter allPhotos]];//this will cause group to reload 
     NSLog(@"how many picture I have in this group: %d",[group numberOfAssets]); 

     if (group!=nil) { 
      [group enumerateAssetsUsingBlock:groupEnumerAtion]; 
     } 
    [self updatephotoList]; 
}; 
+0

gracias por su ayuda – zhangwx

0

Lo que necesita es registrar un observador para ALAssetsLibraryChangedNotification para obtener cambios en la biblioteca. Cuando se active la notificación, vuelva a incluir los grupos y el contenido de AssetsLibrary. Si no se registra para la notificación, su aplicación obtendrá una instantánea anterior de la biblioteca y la enumeración fallará. Tenga en cuenta también que hay un error con respecto a ALAssetsLibraryChangedNotification en iOS 5.x como se documenta aquí: http://www.openradar.me/10484334

+0

No servirá de ayuda si el álbum que estás viendo actualmente se elimina. Sí, recibirá una notificación, pero la aplicación se bloqueará con la excepción 'NSRangeException' cuando intente volver a enumerar. –

+0

Incluso sin cambios de fondo, el 'numberOfAssets' puede ser incorrecto en iOS 8 (GM). – Rivera

+0

Sí, eso es un error. el numberOfAssets lamentablemente incluye el número de fotos borradas/eliminadas en iOS 8 (GM) – holtmann

1

El registro de un observador no ayudó en mi caso. Los usuarios seguían fallando, pero no lo hice. Hasta hoy.

Descubrí la manera de resolver este problema. Al final, es un error en la biblioteca de fotos de Apple, pero hay una solución. Lo que hace es configurar el filtro en la foto y luego en el video, en lugar de dejarlo en los 'activos' predeterminados. A continuación, enumere una vez para cada uno, y haga algunos trucos para asegurarse de obtener ese punto final 'en punto muerto' para hacer las actualizaciones que necesite. Mi enfoque actual es un poco complicado, pero se entiende:

// pending is used to tell the block we're not done, even if result is NULL 
BOOL pending = YES; 
// resorted is just a flag I use in case there are no videos; if there are none, the block is never called at all, and thus the == NULL part never triggers 
__block BOOL resorted = NO; 

ALAssetsGroupEnumerationResultsBlock assetEnumerator = ^(ALAsset *result, NSUInteger index, BOOL *stop) { 
    if(result != NULL) { 
     [assets addObject:result]; 
    } else if (! pending) { 
     // ready!! 
     resorted = YES; 
     [self resort]; // my own method; replace with e.g. tableView reload! 
    } 
}; 

// there are two types of assets - photos and videos; we start with photo 
[group setAssetsFilter:[ALAssetsFilter allPhotos]]; 
NSLog(@"assets = %d", group.numberOfAssets); 
[group enumerateAssetsUsingBlock:assetEnumerator]; 
// we then set pending to NO; even though the enumeration happens in a separate thread, it seems like pending is not caught by the above enumeration (I have 105 images in the group I am enumerating, FWIW; it may be better to set pending in the == NULL part of the enumeration though 
pending = NO; 
// now we switch to video and do the same thing 
[group setAssetsFilter:[ALAssetsFilter allVideos]]; 
BriefLog(@"assets = %d", group.numberOfAssets); 
[group enumerateAssetsUsingBlock:assetEnumerator]; 

// if there are 0 vids, the above block is not ever called so we flip to a background thread and then back (probably not necessary) and then check if resorted is set; if it isn't we call resort 
if (! resorted) dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{ 
    dispatch_async(dispatch_get_main_queue(), ^{ 
     if (! resorted) { 
      [self resort]; // my own method; replace with e.g. tableView reload! 
     } 
    }); 
}); 

Eso es todo. Las NSRangeExceptions desaparecen. Al menos en mi caso lo hicieron.

+1

Su 'pending = NO;' no tiene ningún efecto. Los bloques capturan las variables locales no '' bloqueadas' por valor en el momento en que se crean. – user102008

+0

Estoy bastante seguro de que una de las llamadas recibió un sí y la otra recibió un no. Tendrá que verificar dos veces. – Kalle

0

Empecé con respuesta Qiulang, pero no funcionó para mí. Lo que funcionó para mí es llamar al setAssetsFilter 3 veces seguidas con todas las combinaciones de filtros, antes de comenzar la enumeración.

[group setAssetsFilter:[ALAssetsFilter allPhotos]]; 
[group setAssetsFilter:[ALAssetsFilter allVideos]]; 
[group setAssetsFilter:[ALAssetsFilter allAssets]]; 
+0

Ni esto ni agregar 'numberOfAssets' entre llamadas me funcionó para obtener recuentos de fotos adecuados. – Rivera

0

En iOS 8 También me di cuenta de que numberOfAssets devuelve un número incorrecto de fotos si algunos de ellos fueron borrados y situado en el álbum Eliminado recientemente 'actualmente.

+1

Realmente no es una respuesta sino un comentario. – Rivera

+0

Tuve el mismo problema, se corrigió en iOS 8.0.2 – Ponf

+0

Para mí, numberOfAssets sigue apareciendo mal en iOS8.0.2 – SarpErdag

0

Para aquellos de ustedes que llenar algunos NSArray con todos los ALAssetRepresentations y no quieren cambiar su código demasiado, simplemente filtrar su gama con este Checker - AssetURLChecker

Salud!

0

biblioteca ALAssetsLibrary se depriciated en PHAssetsLibrary a fin de utilizar este código:

__block PHAssetCollection *collection; 
    _arr_downloadedWallpaper = [[NSMutableArray alloc] init]; 

    // Find the album 
    PHFetchOptions *fetchOptions = [[PHFetchOptions alloc] init]; 
    fetchOptions.predicate = [NSPredicate predicateWithFormat:@"title = %@", @"Bhakti Ras"]; 
    collection = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeAlbum 
                  subtype:PHAssetCollectionSubtypeAny 
                  options:fetchOptions].firstObject; 

    PHFetchResult *collectionResult = [PHAsset fetchAssetsInAssetCollection:collection options:nil]; 

    [collectionResult enumerateObjectsUsingBlock:^(PHAsset *asset, NSUInteger idx, BOOL *stop) { 

     //add assets to an array for later use in the uicollectionviewcell 
     NSLog(@"asset is =%@",asset); 


     if (asset) { 
      [self.arr_downloadedWallpaper addObject:asset]; 
     } 
    }]; 
Cuestiones relacionadas