web-dev-qa-db-fra.com

Créer et exporter un gif animé via iOS?

J'ai une série d'images personnalisées par les utilisateurs dans une application iOS qui sont animées dans un style simple de flip book image par image.

Ma question est la suivante: existe-t-il un moyen de permettre aux utilisateurs d'exporter leur animation sous forme de gif animé? Idéalement, je voudrais leur permettre d'envoyer des courriels, de partager des réseaux sociaux (T/FB) ou (pire des cas ..) d'enregistrer un gif animé dans leur dossier de documents pour les récupérer via iTunes.

Je sais comment enregistrer un .png dans la photothèque, et j'ai trouvé un moyen d'enregistrer une animation en tant que fichier QT ( http://www.cimgf.com/2009/02/03/record-your -core-animation-animation / ), mais je n'ai pas trouvé de moyen de simplement lancer un vieux gif animé. Suis-je en train de manquer quelque chose dans Core Animation ou ailleurs? Existe-t-il des approches, des cadres ou des ressources que n'importe qui peut recommander? Désolé si la question est trop générale - du mal à trouver un point de départ.

73
crgt

Vous pouvez créer un GIF animé à l'aide du cadre d'E/S image (qui fait partie du SDK iOS). Vous souhaiterez également inclure le cadre MobileCoreServices, qui définit la constante de type GIF. Vous devez ajouter ces frameworks à votre cible et importer leurs en-têtes dans le fichier où vous souhaitez créer le GIF animé, comme ceci:

#import <ImageIO/ImageIO.h>
#import <MobileCoreServices/MobileCoreServices.h>

C'est plus facile à expliquer par l'exemple. Je vais vous montrer le code que j'ai utilisé pour créer ce GIF sur mon iPhone 5:

animated GIF created by the code shown

Tout d'abord, voici une fonction d'assistance qui prend une taille et un angle et renvoie un UIImage du disque rouge à cet angle:

static UIImage *frameImage(CGSize size, CGFloat radians) {
    UIGraphicsBeginImageContextWithOptions(size, YES, 1); {
        [[UIColor whiteColor] setFill];
        UIRectFill(CGRectInfinite);
        CGContextRef gc = UIGraphicsGetCurrentContext();
        CGContextTranslateCTM(gc, size.width / 2, size.height / 2);
        CGContextRotateCTM(gc, radians);
        CGContextTranslateCTM(gc, size.width / 4, 0);
        [[UIColor redColor] setFill];
        CGFloat w = size.width / 10;
        CGContextFillEllipseInRect(gc, CGRectMake(-w / 2, -w / 2, w, w));
    }
    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return image;
}

Maintenant, nous pouvons créer le GIF. Nous allons d'abord définir une constante pour le nombre d'images, car nous en aurons besoin deux fois plus tard:

static void makeAnimatedGif(void) {
    static NSUInteger const kFrameCount = 16;

Nous aurons besoin d'un dictionnaire de propriétés pour spécifier le nombre de répétitions de l'animation:

    NSDictionary *fileProperties = @{
        (__bridge id)kCGImagePropertyGIFDictionary: @{
            (__bridge id)kCGImagePropertyGIFLoopCount: @0, // 0 means loop forever
        }
    };

Et nous aurons besoin d'un autre dictionnaire de propriétés, que nous attacherons à chaque cadre, en spécifiant la durée d'affichage de ce cadre:

    NSDictionary *frameProperties = @{
        (__bridge id)kCGImagePropertyGIFDictionary: @{
            (__bridge id)kCGImagePropertyGIFDelayTime: @0.02f, // a float (not double!) in seconds, rounded to centiseconds in the GIF data
        }
    };

Nous allons également créer une URL pour le GIF dans notre répertoire de documents:

    NSURL *documentsDirectoryURL = [[NSFileManager defaultManager] URLForDirectory:NSDocumentDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:YES error:nil];
    NSURL *fileURL = [documentsDirectoryURL URLByAppendingPathComponent:@"animated.gif"];

Nous pouvons maintenant créer un CGImageDestination qui écrit un GIF dans l'URL spécifiée:

    CGImageDestinationRef destination = CGImageDestinationCreateWithURL((__bridge CFURLRef)fileURL, kUTTypeGIF, kFrameCount, NULL);
    CGImageDestinationSetProperties(destination, (__bridge CFDictionaryRef)fileProperties);

J'ai découvert que passer fileProperties comme dernier argument de CGImageDestinationCreateWithURL ne fonctionne pas . Vous devez utiliser CGImageDestinationSetProperties.

Nous pouvons maintenant créer et écrire nos cadres:

    for (NSUInteger i = 0; i < kFrameCount; i++) {
        @autoreleasepool {
            UIImage *image = frameImage(CGSizeMake(300, 300), M_PI * 2 * i / kFrameCount);
            CGImageDestinationAddImage(destination, image.CGImage, (__bridge CFDictionaryRef)frameProperties);
        }
    }

Notez que nous passons le dictionnaire des propriétés du cadre avec chaque image du cadre.

Après avoir ajouté exactement le nombre d'images spécifié, nous finalisons la destination et la libérons:

    if (!CGImageDestinationFinalize(destination)) {
        NSLog(@"failed to finalize image destination");
    }
    CFRelease(destination);

    NSLog(@"url=%@", fileURL);
}

Si vous l'exécutez sur le simulateur, vous pouvez copier l'URL à partir de la console de débogage et la coller dans votre navigateur pour voir l'image. Si vous l'exécutez sur l'appareil, vous pouvez utiliser la fenêtre Xcode Organizer pour télécharger le bac à sable de l'application à partir de l'appareil et regarder l'image. Ou vous pouvez utiliser une application comme iExplorer qui vous permet de parcourir directement le système de fichiers de votre appareil. (Cela ne nécessite pas de jailbreak.)

J'ai testé cela sur mon iPhone 5 exécutant iOS 6.1, mais je pense que le code devrait fonctionner aussi loin que iOS 4.0.

J'ai mis tout le code dans ce Gist pour une copie facile.

160
rob mayoff

Pour Swift 3

import Foundation
import UIKit
import ImageIO
import MobileCoreServices

extension UIImage {
    static func animatedGif(from images: [UIImage]) {
        let fileProperties: CFDictionary = [kCGImagePropertyGIFDictionary as String: [kCGImagePropertyGIFLoopCount as String: 0]]  as CFDictionary
        let frameProperties: CFDictionary = [kCGImagePropertyGIFDictionary as String: [(kCGImagePropertyGIFDelayTime as String): 1.0]] as CFDictionary

        let documentsDirectoryURL: URL? = try? FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
        let fileURL: URL? = documentsDirectoryURL?.appendingPathComponent("animated.gif")

        if let url = fileURL as CFURL? {
            if let destination = CGImageDestinationCreateWithURL(url, kUTTypeGIF, images.count, nil) {
                CGImageDestinationSetProperties(destination, fileProperties)
                for image in images {
                    if let cgImage = image.cgImage {
                        CGImageDestinationAddImage(destination, cgImage, frameProperties)
                    }
                }
                if !CGImageDestinationFinalize(destination) {
                    print("Failed to finalize the image destination")
                }
                print("Url = \(fileURL)")
            }
        }
    }
}

Je l'ai converti à partir de réponse ci-dessus . J'espère que ça aide.

Disponible en Gist .

Les modifications sont les bienvenues.

1
Nikhil Manapure

Si vous recherchez une solution Swift 3, vous pouvez jeter un œil à https://github.com/onmyway133/GifMagic . Elle a Encoder et Decoder qui assemble et désassemble le fichier gif.

Fondamentalement, vous devez utiliser Image IO framework avec ces fonctions CGImageDestinationCreateWithURL, CGImageDestinationSetProperties, CGImageDestinationAddImage, CGImageDestinationFinalize

Aussi avec Swift https://developer.Apple.com/library/content/documentation/Swift/Conceptual/BuildingCocoaApps/WorkingWithCocoaDataTypes.html

Les objets Core Foundation renvoyés par les API annotées sont automatiquement gérés en mémoire dans Swift: vous n'avez pas besoin d'appeler vous-même les fonctions CFRetain, CFRelease ou CFAutorelease.

0
onmyway133