Создание и экспорт анимированного gif через iOS?
У меня есть ряд пользовательских изображений в приложении iOS, которые анимируются в простом, кадровом стиле флип-книги.
мой вопрос таков: есть ли способ разрешить пользователям экспортировать свою анимацию в виде анимированного gif? В идеале, я бы хотел, чтобы они могли отправлять электронную почту, социальную долю (T/FB) или (в худшем случае..) сохраните анимированный gif в папку "Документы" для извлечения через iTunes.
Я знаю, как спасти .png в библиотеку фотографий, и я нашел способ для записи анимации в виде файла QT (http://www.cimgf.com/2009/02/03/record-your-core-animation-animation/), но я не нашел способ просто выгнать простой старый анимированный gif. Я что-то пропустил в основной анимации или где-то еще? Существуют ли какие-либо подходы, рамки или ресурсы, которые кто-либо может рекомендовать? Извините, если вопрос слишком общий - изо всех сил пытался найти отправную точку. Любая помощь ценится.
3 ответа:
вы можете создать анимированный GIF с помощью Image I/O framework (который является частью iOS SDK). Вы также хотите включить
MobileCoreServices
фреймворк, который определяет константу типа GIF. Вам нужно добавить эти фреймворки в свою цель и импортировать их заголовки в файл, где вы хотите создать анимированный GIF, например:#import <ImageIO/ImageIO.h> #import <MobileCoreServices/MobileCoreServices.h>
это проще всего объяснить на примере. Я покажу вам код, который я использовал, чтобы сделать этот GIF на моем iPhone 5:
во-первых, вот вспомогательная функция, которая принимает размер и угол и возвращает
UIImage
красного диска под этим углом: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; }
теперь мы можем создать GIF. Сначала мы определим константу для количества кадров, потому что нам это нужно дважды позже:
static void makeAnimatedGif(void) { static NSUInteger const kFrameCount = 16;
нам понадобится словарь свойств, чтобы указать, сколько раз анимация должна повторяться:
NSDictionary *fileProperties = @{ (__bridge id)kCGImagePropertyGIFDictionary: @{ (__bridge id)kCGImagePropertyGIFLoopCount: @0, // 0 means loop forever } };
и нам понадобится еще одно свойство словарь, который мы прикрепим к каждому кадру, указав, как долго этот кадр должен отображаться:
NSDictionary *frameProperties = @{ (__bridge id)kCGImagePropertyGIFDictionary: @{ (__bridge id)kCGImagePropertyGIFDelayTime: @0.02f, // a float (not double!) in seconds, rounded to centiseconds in the GIF data } };
мы также создадим URL для GIF в нашем каталоге документов:
NSURL *documentsDirectoryURL = [[NSFileManager defaultManager] URLForDirectory:NSDocumentDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:YES error:nil]; NSURL *fileURL = [documentsDirectoryURL URLByAppendingPathComponent:@"animated.gif"];
теперь мы можем создать
CGImageDestination
который записывает GIF на указанный URL:CGImageDestinationRef destination = CGImageDestinationCreateWithURL((__bridge CFURLRef)fileURL, kUTTypeGIF, kFrameCount, NULL); CGImageDestinationSetProperties(destination, (__bridge CFDictionaryRef)fileProperties);
я обнаружил, что проезжает
fileProperties
как последний аргументCGImageDestinationCreateWithURL
тут не работа. Вы должны использоватьCGImageDestinationSetProperties
.теперь мы можем создавать и писать наши кадры:
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); } }
обратите внимание, что мы передаем словарь свойств кадра вместе с каждым изображением кадра.
после того, как мы добавили точно указанное количество кадров, мы завершаем назначение и выпускаем его:
if (!CGImageDestinationFinalize(destination)) { NSLog(@"failed to finalize image destination"); } CFRelease(destination); NSLog(@"url=%@", fileURL); }
если вы запустите это на симуляторе, вы можете скопировать URL-адрес из консоли отладки и вставить его в свой браузер, чтобы увидеть изображение. Если вы запустите его на устройстве, вы можете использовать окно Xcode Organizer для загрузки песочницы приложения из устройство и посмотрите на изображение. Или вы можете использовать приложение, как
iExplorer
это позволяет просматривать файловую систему вашего устройства непосредственно. (Это не требует джейлбрейка.)я тестировал это на своем iPhone 5 под управлением iOS 6.1, но я считаю, что код должен работать еще до iOS 4.0.
я положил весь код в в этом суть для легкого копирования.
если вы ищете решение Swift 3, Вы можете взглянуть на https://github.com/onmyway133/GifMagic. он имеет
Encoder
иDecoder
который собирает и разбирает gif-файл.в принципе, вы должны использовать
Image IO
база с этими функциямиCGImageDestinationCreateWithURL
,CGImageDestinationSetProperties
,CGImageDestinationAddImage
,CGImageDestinationFinalize
также с быстрым https://developer.apple.com/library/content/documentation/Swift/Conceptual/BuildingCocoaApps/WorkingWithCocoaDataTypes.html
объекты Core Foundation, возвращаемые из аннотированных API, автоматически управляются памятью в Swift-вам не нужно самостоятельно вызывать функции CFRetain, CFRelease или CFAutorelease.
на 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)") } } } }
я преобразовал его из ответ выше. Надеюсь, это поможет.
доступно как суть.
изменения приветствуются.