Jak zapisać zdjęcie do biblioteki zdjęć iPhone'a?

193

Co muszę zrobić, aby zapisać obraz wygenerowany przez mój program (prawdopodobnie z aparatu, a może nie) w bibliotece zdjęć systemowych na iPhonie?

jblocksom
źródło
Możesz sprawdzić ten kod . Świetny dzień!
Celil Bozkurt

Odpowiedzi:

411

Możesz użyć tej funkcji:

UIImageWriteToSavedPhotosAlbum(UIImage *image, 
                               id completionTarget, 
                               SEL completionSelector, 
                               void *contextInfo);

Potrzebujesz tylko uzupełniania Cel , kompletowania i kontekstu, jeśli chcesz być powiadamiany o zakończeniu UIImagezapisywania, w przeciwnym razie możesz przekazaćnil .

Zobacz oficjalną dokumentację dlaUIImageWriteToSavedPhotosAlbum() .

Martin Gordon
źródło
Weź +1 za dokładną odpowiedź
Niru Mukund Shah,
Cześć dzięki za świetne rozwiązanie. Tutaj mam jedną wątpliwość, jak uniknąć duplikatów podczas zapisywania obrazu w bibliotece zdjęć. Z góry dziękuję.
Naresh
Jeśli chcesz oszczędzać w lepszej jakości, zobacz to: stackoverflow.com/questions/1379274/…
eonil
4
Musisz teraz dodać „Prywatność - Opis użycia biblioteki zdjęć” w systemie iOS 11, aby zapisać zdjęcia w albumie użytkowników.
jeździectwo
1
jak nadać nazwę zapisanym obrazom?
Priyal
63

Przestarzałe w iOS 9.0.

Jest znacznie szybszy niż UIImageWriteToSavedPhotosAlbum sposób na zrobienie tego przy użyciu frameworku AssetsLibrary systemu iOS 4.0+

    ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];

    [library writeImageToSavedPhotosAlbum:[image CGImage] orientation:(ALAssetOrientation)[image imageOrientation] completionBlock:^(NSURL *assetURL, NSError *error){
    if (error) {
    // TODO: error handling
    } else {
    // TODO: success handling
    }
}];
[library release];
Denis Fileev
źródło
1
Czy istnieje sposób na zapisanie dowolnych metadanych wraz ze zdjęciem?
zakdance
2
Próbowałem oszczędzać za pomocą ALAssetsLibrary, zapisywanie zajmuje tyle samo czasu UIImageWriteToSavedPhotosAlbum.
Hlung
I to zawiesza kamerę :( Chyba nie jest obsługiwane tło?
Hlung
Ten jest o wiele czystszy, ponieważ możesz użyć bloku do obsługi zakończenia.
jpswain
5
Korzystam z tego kodu i dołączam ten framework #import <AssetsLibrary / AssetsLibrary.h>, a nie AVFoundation. Czy nie należy edytować odpowiedzi? @Denis
Julian Osorio
30

Najprostszym sposobem jest:

UIImageWriteToSavedPhotosAlbum(myUIImage, nil, nil, nil);

Dla Swift, można zwrócić się do zapisywania na iOS biblioteki zdjęć przy użyciu szybkich

Mutawe
źródło
3
Naprawdę podoba mi się twoja ikona profilu użytkownika SO. Całkiem fajny obraz Xcode.
Supertecnoboff
1
Fantastycznie proste i bardzo, bardzo łatwe!
Septronic
13

Pamiętaj o jednej rzeczy: jeśli korzystasz z funkcji oddzwaniania, upewnij się, że selektor jest zgodny z następującą formą:

- (void) image: (UIImage *) image didFinishSavingWithError: (NSError *) error contextInfo: (void *) contextInfo;

W przeciwnym razie nastąpi awaria z błędem, takim jak następujący:

[NSInvocation setArgument:atIndex:]: index (2) out of bounds [-1, 1]

Jeff C.
źródło
10

Po prostu przekaż mu obrazy z tablicy w ten sposób

-(void) saveMePlease {

//Loop through the array here
for (int i=0:i<[arrayOfPhotos count]:i++){
         NSString *file = [arrayOfPhotos objectAtIndex:i];
         NSString *path = [get the path of the image like you would in DOCS FOLDER or whatever];
         NSString *imagePath = [path stringByAppendingString:file];
         UIImage *image = [[[UIImage alloc] initWithContentsOfFile:imagePath]autorelease];

         //Now it will do this for each photo in the array
         UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil);
        }
}

Przepraszam za literówki, po prostu zrobiłem to w locie, ale masz rację

mrburns05
źródło
Korzystanie z tego spowoduje pominięcie niektórych zdjęć, próbowałem. Prawidłowym sposobem jest użycie funkcji oddzwaniania z selektora zakończenia.
SamChen,
1
czy możemy zapisać obrazy o niestandardowej nazwie?
Użytkownik 1531343,
Nigdy nie należy do tego używać pętli. Prowadzi to do stanu wyścigu i awarii.
saurabh
4

Podczas zapisywania tablicy zdjęć nie używaj pętli for, wykonaj następujące czynności

-(void)saveToAlbum{
   [self performSelectorInBackground:@selector(startSavingToAlbum) withObject:nil];
}
-(void)startSavingToAlbum{
   currentSavingIndex = 0;
   UIImage* img = arrayOfPhoto[currentSavingIndex];//get your image
   UIImageWriteToSavedPhotosAlbum(img, self, @selector(image:didFinishSavingWithError:contextInfo:), nil);
}
- (void)image: (UIImage *) image didFinishSavingWithError: (NSError *) error contextInfo: (void *) contextInfo{ //can also handle error message as well
   currentSavingIndex ++;
   if (currentSavingIndex >= arrayOfPhoto.count) {
       return; //notify the user it's done.
   }
   else
   {
       UIImage* img = arrayOfPhoto[currentSavingIndex];
       UIImageWriteToSavedPhotosAlbum(img, self, @selector(image:didFinishSavingWithError:contextInfo:), nil);
   }
}
SamChen
źródło
4

W Swift :

    // Save it to the camera roll / saved photo album
    // UIImageWriteToSavedPhotosAlbum(self.myUIImageView.image, nil, nil, nil) or 
    UIImageWriteToSavedPhotosAlbum(self.myUIImageView.image, self, "image:didFinishSavingWithError:contextInfo:", nil)

    func image(image: UIImage!, didFinishSavingWithError error: NSError!, contextInfo: AnyObject!) {
            if (error != nil) {
                // Something wrong happened.
            } else {
                // Everything is alright.
            }
    }
King-Wizard
źródło
tak ... miło .. ale po zapisaniu obrazu chcę załadować obraz z galerii ... jak to zrobić
EI Captain v2.0
4

Poniższa funkcja będzie działać. Możesz skopiować stąd i wkleić tam ...

-(void)savePhotoToAlbum:(UIImage*)imageToSave {

    CGImageRef imageRef = imageToSave.CGImage;
    NSDictionary *metadata = [NSDictionary new]; // you can add
    ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];

    [library writeImageToSavedPhotosAlbum:imageRef metadata:metadata completionBlock:^(NSURL *assetURL,NSError *error){
        if(error) {
            NSLog(@"Image save eror");
        }
    }];
}
iDevAmit
źródło
2

Szybki 4

func writeImage(image: UIImage) {
    UIImageWriteToSavedPhotosAlbum(image, self, #selector(self.finishWriteImage), nil)
}

@objc private func finishWriteImage(_ image: UIImage, didFinishSavingWithError error: NSError?, contextInfo: UnsafeRawPointer) {
    if (error != nil) {
        // Something wrong happened.
        print("error occurred: \(String(describing: error))")
    } else {
        // Everything is alright.
        print("saved success!")
    }
}
luhuiya
źródło
1

moja ostatnia odpowiedź to zrobi ..

dla każdego obrazu, który chcesz zapisać, dodaj go do NSMutableArray

    //in the .h file put:

NSMutableArray *myPhotoArray;


///then in the .m

- (void) viewDidLoad {

 myPhotoArray = [[NSMutableArray alloc]init];



}

//However Your getting images

- (void) someOtherMethod { 

 UIImage *someImage = [your prefered method of using this];
[myPhotoArray addObject:someImage];

}

-(void) saveMePlease {

//Loop through the array here
for (int i=0:i<[myPhotoArray count]:i++){
         NSString *file = [myPhotoArray objectAtIndex:i];
         NSString *path = [get the path of the image like you would in DOCS FOLDER or whatever];
         NSString *imagePath = [path stringByAppendingString:file];
         UIImage *image = [[[UIImage alloc] initWithContentsOfFile:imagePath]autorelease];

         //Now it will do this for each photo in the array
         UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil);
        }
}
mrburns05
źródło
Wypróbowałem twoje rozwiązanie, zawsze brakowało niektórych zdjęć. Zobacz moją odpowiedź. link
SamChen,
1
homeDirectoryPath = NSHomeDirectory();
unexpandedPath = [homeDirectoryPath stringByAppendingString:@"/Pictures/"];

folderPath = [NSString pathWithComponents:[NSArray arrayWithObjects:[NSString stringWithString:[unexpandedPath stringByExpandingTildeInPath]], nil]];

unexpandedImagePath = [folderPath stringByAppendingString:@"/image.png"];

imagePath = [NSString pathWithComponents:[NSArray arrayWithObjects:[NSString stringWithString:[unexpandedImagePath stringByExpandingTildeInPath]], nil]];

if (![[NSFileManager defaultManager] fileExistsAtPath:folderPath isDirectory:NULL]) {
    [[NSFileManager defaultManager] createDirectoryAtPath:folderPath attributes:nil];
}
dzień dobry
źródło
Ta odpowiedź jest nieprawidłowa, ponieważ nie zapisuje obrazu w systemowej bibliotece zdjęć, ale w piaskownicy.
Evan
1

Utworzyłem w tym celu kategorię UIImageView na podstawie niektórych powyższych odpowiedzi.

Plik nagłówka:

@interface UIImageView (SaveImage) <UIActionSheetDelegate>
- (void)addHoldToSave;
@end

Realizacja

@implementation UIImageView (SaveImage)
- (void)addHoldToSave{
    UILongPressGestureRecognizer* longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleLongPress:)];
    longPress.minimumPressDuration = 1.0f;
    [self addGestureRecognizer:longPress];
}

-  (void)handleLongPress:(UILongPressGestureRecognizer*)sender {
    if (sender.state == UIGestureRecognizerStateEnded) {

        UIActionSheet* _attachmentMenuSheet = [[UIActionSheet alloc] initWithTitle:nil
                                                                          delegate:self
                                                                 cancelButtonTitle:@"Cancel"
                                                            destructiveButtonTitle:nil
                                                                 otherButtonTitles:@"Save Image", nil];
        [_attachmentMenuSheet showInView:[[UIView alloc] initWithFrame:self.frame]];
    }
    else if (sender.state == UIGestureRecognizerStateBegan){
        //Do nothing
    }
}
-(void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex{
    if  (buttonIndex == 0) {
        UIImageWriteToSavedPhotosAlbum(self.image, nil,nil, nil);
    }
}


@end

Teraz wystarczy wywołać tę funkcję w widoku obrazu:

[self.imageView addHoldToSave];

Opcjonalnie możesz zmienić parametr minimumPressDuration.

HugglesNL
źródło
1

W Swift 2.2

UIImageWriteToSavedPhotosAlbum(image: UIImage, _ completionTarget: AnyObject?, _ completionSelector: Selector, _ contextInfo: UnsafeMutablePointer<Void>)

Jeśli nie chcesz otrzymywać powiadomień o zakończeniu zapisywania obrazu, możesz przekazać zero w parametrze uzupełniania Cel , uzupełnienie i kontekst Informacje parametrów.

Przykład:

UIImageWriteToSavedPhotosAlbum(image, self, #selector(self.imageSaved(_:didFinishSavingWithError:contextInfo:)), nil)

func imageSaved(image: UIImage!, didFinishSavingWithError error: NSError?, contextInfo: AnyObject?) {
        if (error != nil) {
            // Something wrong happened.
        } else {
            // Everything is alright.
        }
    }

Ważną rzeczą do zapamiętania jest to, że twoja metoda, która obserwuje zapisywanie obrazu, powinna mieć te 3 parametry, w przeciwnym razie wystąpią błędy NSInvocation.

Mam nadzieję, że to pomoże.

jarora
źródło
0

Możesz tego użyć

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
   UIImageWriteToSavedPhotosAlbum(img.image, nil, nil, nil);
});
Pratik Somaiya
źródło