Как сохранить созданный gif в фотографии? (сейчас работает только в первый раз)

В моем анимационном приложении я хочу сохранить анимацию в формате gif в приложении «Фотографии». Прямо сейчас код, который у меня есть, успешно сохраняет его в фотографии с первого раза, но после этого он генерирует ошибку (ошибка PHPhotosErrorDomain -1.) ФункцияgenerateGifFromImages, похоже, работает, потому что я могу найти все файлы gif в папке назначения, но (кроме того, что я это делаю впервые) они просто не сохраняются в фотографии.

Я использую этот код для создания GIF из массива изображений:

import UIKit
import MobileCoreServices

public class GIFFromImages {
    public enum colorSpace {
        case rgb
        case gray
    }
    public init () {}
}

extension GIFFromImages {
    public func makeFileURL(filename: String) -> URL {
        let gifDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
        let gifFileURL = gifDirectory.appendingPathComponent(filename)
        return gifFileURL
    }
    
    public func generateGifFromImages(images: [UIImage], fileURL: URL, colorSpace: colorSpace, delayTime: Double, loopCount: Int) {
        let gifGroup = DispatchGroup()
        var tempImages: [UIImage] = []
        for image in images {
            gifGroup.enter()
            let imageWidth = UIScreen.main.bounds.width
            let imageHeight = UIScreen.main.bounds.height
            
            let imageRect: CGRect = CGRect(x:0, y:0, width: imageWidth, height: imageHeight)
            let imageBitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.none.rawValue)
            let context = CGContext(data: nil, width: Int(imageWidth), height: Int(imageHeight), bitsPerComponent: 8, bytesPerRow: 0, space: CGColorSpaceCreateDeviceGray(), bitmapInfo: imageBitmapInfo.rawValue)
            
            
            
            if let cgImg = image.cgImage {
                
                //Set bg as white
                context?.setFillColor(UIColor.white.cgColor)
                context?.fill(imageRect)
                
                context?.draw(cgImg, in: imageRect)
                if let makeImg = context?.makeImage() {
                    let imageRef = makeImg
                    let newImage = UIImage(cgImage: imageRef)
                    tempImages.append(newImage)
                    gifGroup.leave()
                }
            }
        }
        
        gifGroup.notify(queue: .main) {
            let gifFileProperties: CFDictionary = [kCGImagePropertyGIFDictionary as String: [kCGImagePropertyGIFLoopCount as String: loopCount]]  as CFDictionary
            let gifFrameProperties: CFDictionary = [kCGImagePropertyGIFDictionary as String: [kCGImagePropertyGIFDelayTime as String: delayTime]] as CFDictionary
            if let url = fileURL as CFURL? {
                if let destination = CGImageDestinationCreateWithURL(url, kUTTypeGIF, images.count, nil) {
                    CGImageDestinationSetProperties(destination, gifFileProperties)
                    for image in tempImages {
                        if let cgImage = image.cgImage {
                            CGImageDestinationAddImage(destination, cgImage, gifFrameProperties)
                        }
                    }
                    if !CGImageDestinationFinalize(destination) {
                        print("Failed to finalize the image destination")
                    }
                }
            }
        }
    }
}


И я вызываю его и сохраняю в фотографии с этой кнопки:

Button {
                    
                    print("Saving animation titled: \(animation.title)")
                    
                    var images: [UIImage] = []
                    
                    for frame in animation.frames {
                        let image = try! PKDrawing(data: frame.frameData).generateThumbnail(scale: 1)
                        images.append(image)
                    }
                    
                    let timestamp = Date().timeIntervalSince1970
                    let timestampString = String(format: "%.0f", timestamp)
                    let gifURL = gifManager.makeFileURL(filename: "\(timestampString).gif")
                    
                    gifManager.generateGifFromImages(images: images, fileURL: gifURL, colorSpace: .rgb, delayTime: (1.0/Double(animation.framesPerSecond)), loopCount: 0)
                    
                    
                    // Save the generated GIF to the Photos library
                    PHPhotoLibrary.shared().performChanges({
                        
                        // Check if the GIF file exists
                        if FileManager.default.fileExists(atPath: gifURL.path) {
                            print("GIF file exists at:", gifURL.path)
                        } else {
                            print("Error: GIF file does not exist at:", gifURL.path)
                        }
                        
                        PHAssetChangeRequest.creationRequestForAssetFromImage(atFileURL: gifURL)
                    }) { success, error in
                        if success {
                            print("GIF saved successfully.")
                        } else {
                            print("Error saving GIF:", error?.localizedDescription ?? "Unknown error")
                        }
                    }
                    
                    close()
                    
                } label: {
                    
                    ZStack {
                        Rectangle()
                            .frame(width: 400, height: 100)
                            .foregroundColor(.accentColor)
                            .cornerRadius(30)
                        Text("Save GIF to Photos")
                            .foregroundStyle(.white)
                            .font(.title)
                            .bold()
                    } 
                }

Вы почти никогда не захотите использовать localizedDescription. Распечатка всей ошибки даст вам больше информации.

jnpdx 25.04.2024 17:13

@jnpdx спасибо. Изменение дает мне следующее: Ошибка сохранения GIF: Error Domain=PHPhotosErrorDomain Code=-1 "(null)" Есть ли способ интерпретировать это?

Zoe Sosa 25.04.2024 17:50

iOS не имеет встроенных возможностей для работы с анимированными GIF-файлами. Я бы вообще не ожидал, что в библиотеке фотографий можно будет хранить анимированный GIF-файл; это не фотография в каком-либо смысле, который может понять iOS. Я бы предложил сохранить его где-нибудь в другом месте.

matt 25.04.2024 20:01

@matt, спасибо, но я не думаю, что это проблема. В первый раз GIF сохраняется, как и ожидалось, и у меня есть другие приложения, которые позволяют сохранять GIF-изображения в фотографии.

Zoe Sosa 25.04.2024 23:39
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
4
62
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Я нашел решение: я сделал имя файла одинаковым для всех файлов, которые я сохраняю (чтобы оно переопределяло последний файл вместо добавления дополнительного хранилища), затем я добавил обработчик завершения в функциюgenerateGifFromImages, а затем последовал этому ответу. чтобы заменить мой код для сохранения gif в фотографиях: Сохранить gif в библиотеке фотографий iOS в Swift

Другие вопросы по теме