2013-02-16 19 views
66

Ho una serie di immagini personalizzate dall'utente all'interno di un'app per iOS animate in un semplice stile flip book frame-by-frame.Crea ed esporta una gif animata tramite iOS?

La mia domanda è questa: c'è un modo per consentire agli utenti di esportare la propria animazione come una gif animata? Idealmente, mi piacerebbe abilitarli a inviare e-mail, condivisione sociale (T/FB) o (caso peggiore ..) salvare una gif animata nella loro cartella documenti per il recupero tramite iTunes.

So come salvare un file .png nella libreria di foto e ho trovato un modo per registrare un'animazione come file QT (http://www.cimgf.com/2009/02/03/record-your-core-animation-animation/), ma non ho trovato il modo di estrarre un semplice vecchio animato gif. Mi manca qualcosa in Core Animation o da qualche altra parte? Ci sono approcci, strutture o risorse che chiunque può consigliare? Scusa se la domanda è troppo generica, faticando a trovare un punto di partenza. Qualsiasi aiuto apprezzato.

risposta

157

È possibile creare una GIF animata utilizzando il framework Image I/O (che fa parte dell'SDK di iOS). Dovrai anche includere il framework MobileCoreServices, che definisce la costante di tipo GIF. È necessario aggiungere questi quadri al vostro obiettivo, e importare le loro intestazioni nel file di cui si desidera creare l'immagine GIF animata, in questo modo:

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

E 'più facile da spiegare con l'esempio. Ti mostrerò il codice che ho usato per fare questa GIF sul mio iPhone 5:

animated GIF created by the code shown

In primo luogo, ecco una funzione di supporto che prende una dimensione e un angolo e restituisce un UIImage del disco rosso in quel angolo:

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; 
} 

Ora possiamo creare la GIF. Per prima cosa definiamo una costante per il numero di fotogrammi, perché abbiamo bisogno di due volte in seguito:

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

avremo bisogno di un dizionario di proprietà per specificare il numero di volte in cui l'animazione deve ripetere:

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

E avremo bisogno di un altro dizionario proprietà, che ci attribuiamo ad ogni fotogramma, specificando quanto tempo quel lasso dovrebbe essere visualizzato:

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

creeremo anche un URL per il GIF in nostro Do cumenti directory:

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

Ora possiamo creare una CGImageDestination che scrive una GIF all'URL specificato:

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

ho scoperto che passando fileProperties come ultimo argomento di CGImageDestinationCreateWithURL fa non lavoro. Devi usare CGImageDestinationSetProperties.

Ora siamo in grado di creare e scrivere i nostri telai:

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); 
     } 
    } 

Nota che passiamo le proprietà del frame dictionary lungo ogni immagine frame con.

Dopo abbiamo aggiunto esattamente il numero di fotogrammi specificato, abbiamo finalizzare la destinazione e rilasciarlo:

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

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

Se si esegue questo sul simulatore, è possibile copiare l'URL dalla console di debug e incolla nel tuo browser per vedere l'immagine. Se lo si esegue sul dispositivo, è possibile utilizzare la finestra Xcode Organizer per scaricare la sandbox dell'app dal dispositivo e osservare l'immagine. Oppure puoi utilizzare un'app come iExplorer che ti consente di sfogliare direttamente il filesystem del dispositivo. (Questo non richiede il jailbreak.)

Ho provato questo sul mio iPhone 5 con iOS 6.1, ma credo che il codice dovrebbe funzionare fin da iOS 4.0.

Ho messo tutto il codice in this gist per una facile copia.

+0

Questo è qualcosa che stavo cercando da un po '. (Creazione di GIF animate). La generazione effettiva dell'immagine è abbastanza banale e abbastanza facile da sostituire con il codice dalla mia applicazione. Il difficile è creare l'immagine GIF attuale e mi hai dato tutto ciò di cui ho bisogno per farlo. Grazie! (Votato) –

+0

D'accordo con Duncan - grazie Rob! Devo ancora dare un vortice a questa sera, ma è tutto quello che stavo cercando ... grazie ancora !! – crgt

+1

Rob, oggi ho lavorato con la tua tecnica, ma c'è un grosso limite. Il sistema conserva i dati per tutte le immagini nella sequenza di animazione in memoria fino al completamento della chiamata finalizzata. Sto creando alcune sequenze GIF piuttosto grandi, e con 30 FPS non ci vuole molto per esaurire memoria e crash. C'è un modo per tamponare i frame su disco in qualche modo? Sto creando i miei CGImages da OpenGL e utilizzando un fornitore di dati. Esiste una forma di fornitore di dati che legge i contenuti da un file. Mi chiedo se leggerà ogni frame a turno e lo rilascerà mentre finalizza la GIF. –

0

Se stai cercando la soluzione Swift 3, puoi dare un'occhiata a https://github.com/onmyway133/GifMagic. Ha Encoder e Decoder che assembla e disassembla il file gif.

In sostanza, si dovrebbe usare Image IO quadro con queste funzioni CGImageDestinationCreateWithURL, CGImageDestinationSetProperties, CGImageDestinationAddImage, CGImageDestinationFinalize

Anche con Swift https://developer.apple.com/library/content/documentation/Swift/Conceptual/BuildingCocoaApps/WorkingWithCocoaDataTypes.html

Nucleo Fondazione oggetti restituiti dalle API annotati vengono automaticamente memoria gestita a Swift-te non è necessario richiamare da soli le funzioni CFRetain, CFRelease o CFAutorelease.

0

Per 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)") 
      } 
     } 
    } 
} 

Ho convertito dalla above answer. Spero possa essere d'aiuto.

Disponibile come gist.

Le modifiche sono benvenute.

Problemi correlati