2

URLからいくつかの画像を取得し、それらをUIImageに変換してから、フォトライブラリに追加してから、カスタムアルバムに追加するアプリケーションを作成しています。カメラロールに入れずにカスタムアルバムに追加することは不可能だと思いますので、不可能だと思います(ただし、可能であれば理想的です)。

私の問題は、このサイトのコードを使用していて機能することですが、大きな写真を処理すると、「WriteBusy」として数枚返されます。関数を独自の完了コード内にコピーしてから、次のコード内にコピーして6まで続けると、すべてを正常に保存できます(最も多く見たのは、3〜4でしたが、サイズはわかりません。画像と私はいくつかの本当に大きなものを手に入れることができました)-これは、この段階でもエラーが発生し、それを繰り返すためのブロックがなかったため、カスタムアルバムにすべて含まれていないという問題につながりました。

エラーが表示される前にコードがすべて完了すると、実際の画像の保存がバックグラウンドスレッドに移動することを理解しています(ただし、これは特に設定していません)が、理想的には、単一のバックグラウンドに保存する画像をキューに入れる必要がありますスレッド化するため、同期的に発生しますが、UIをフリーズしません。

私のコードは次のようになります:

UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:singleImage]]];
    [self.library saveImage:image toAlbum:@"Test Album" withCompletionBlock:^(NSError *error) {
        if (error!=nil) {
            NSLog(@"Error");
        }
    }];

コードの繰り返しを削除しました。そうしないと、コードサンプルが非常に長くなります。以前はNSLogコードが存在していた場所でした。

私のテストサンプルでは25枚の画像を扱っていますが、これは簡単に200枚程度で、非常に高解像度になる可能性があるため、複数の画像を見逃すことなく、これを何度も確実に実行できるものが必要です。

ありがとうロブ

4

2 に答える 2

1

保存画像コードを取り除き、オブジェクト上の配列で再帰的にそれ自体を呼び出す独自の関数に移動することで、それを機能させることができました。失敗した場合は、正常に機能するまで同じ画像を関数に再解析します。完了すると「完了」と表示されます。ループを完了するために関数fromのcompletedBlock:を使用しているため、実行ごとに1つのファイルのみが保存されます。

これは私が再帰的に使用したコードです:

- (void)saveImage {

if(self.thisImage)
{
    [self.library saveImage:self.thisImage toAlbum:@"Test Album" withCompletionBlock:^(NSError *error) {
        if (error!=nil) {
            [self saveImage];
        }
        else
        {
            [self.imageData removeObject:self.singleImageData];
            NSLog(@"Success!");
            self.singleImageData = [self.imageData lastObject];
            self.thisImage = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:self.singleImageData]]];
            [self saveImage];
        }
    }];
}
else
{
    self.singleImageData = nil;
    self.thisImage = nil;
    self.imageData = nil;
    self.images = nil;
    NSLog(@"Done!");
}

}

これを設定するために、私はもともとUIImagesの配列を使用しましたが、これは大量のメモリを使用し、非常に低速でした(最大400枚の写真をテストしていました)。これを行うためのはるかに優れた方法は、URLのNSMutableArrayをNSStringとして格納してから、関数内でNSDataGETを実行することでした。

次のコードは、データを使用してNSMutableArrayを設定し、関数を呼び出すものです。また、最初のUIImageをメモリに設定し、self.thisImageの下に保存します。

NSEnumerator *e = [allDataArray objectEnumerator];
NSDictionary *object;

while (object = [e nextObject]) {
    NSArray *imagesArray = [object objectForKey:@"images"];
    NSString *singleImage = [[imagesArray objectAtIndex:0] objectForKey:@"source"];
    [self.imageData addObject:singleImage];
}

self.singleImageData = [self.imageData lastObject];
self.thisImage = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:self.singleImageData]]];
[self saveImage];

これは、UIImageの残りのゲッターを関数に含めることができ、UIImageの単一インスタンスを監視できることを意味します。また、生のURLをself.singleImageDataに記録して、配列から正しい要素を削除して重複を停止できるようにします。

これらは私が使用した変数です:

self.images = [[NSMutableArray alloc] init];
self.thisImage = [[UIImage alloc] init];
self.imageData = [[NSMutableArray alloc] init];
self.singleImageData = [[NSString alloc] init];

この回答は、iOS 6(iOS 6.1でテスト済み)でhttp://www.touch-code-magazine.com/ios5-saving-photos-in-custom-photo-album-category-for-download/を使用しているすべての人に有効です。そして、すべての写真がエラーなしで正しく保存されるはずです。

于 2013-03-04T20:06:51.677 に答える
0

saveImage:toAlbum:withCompletionBlock の場合、dispatch_async を使用しています。I/O 操作で生成されるスレッドが多すぎるのではないかと心配しています。トリガーする各書き込みタスクは、前のタスクによってブロックされます (同じキューで I/O を実行しているため)。新しいスレッドを作成します (通常、 global_queue の dispatch_async は、最適化された数のスレッドを使用して gcd によって最適化されます)。

セマフォを使用して同時に書き込み操作を固定数に制限するか、iOS 5 から利用できる dispatch_io_ 関数を使用する必要があります。両方の方法でこれを行う方法の例がたくさんあります。

アイデアを与えるためのオンザフライコード:

 dispatch_semaphore_t aSemaphore = dispatch_semaphore_create(4);
 dispatch_queue_t ioQueue = dispatch_queue_create("com.customqueue", NULL);

 // dispatch the following block to the ioQueue
 // ( for loop with all images )
 dispatch_semaphore_wait(aSemaphore , DISPATCH_TIME_FOREVER);
 [self.library saveImage:image 
                 toAlbum:@"Test Album"
     withCompletionBlock:^(NSError *error){
          dispatch_semaphore_signal(aSemaphore);
     }];

そのため、最大 4 つの saveImage:toAlbum があるたびに、1 つが完了するとすぐに別のものが開始されます。上記 (ioQueue) のように、イメージで for ループを実行するコードをディスパッチするカスタム キューを作成する必要があるため、セマフォが待機しているときにメイン スレッドがブロックされません。

于 2013-03-03T23:52:24.247 に答える