Motion not available ios 26 live photo

2 weeks ago 24
ARTICLE AD BOX

I’m trying to set a Live Photo as a live wallpaper on iOS. I’ve saved the Live Photo to my Photos library, but when I attempt to set it as the wallpaper, the Live Photo effect option is grayed out.

I try all scenarios for correct video; 3second 1 second
I downloaded a video from a livewallapaper app and i use it because of i thnik my video is not correct. Still dont working.

import Foundation import UIKit import Photos import React import AVFoundation import MobileCoreServices @objc(LivePhotoModule) class LivePhotoModule: NSObject { // MARK: - React Native Bridge Method @objc func saveLivePhoto(_ videoUri: String, albumName: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { let fileURL = URL(fileURLWithPath: videoUri) guard FileManager.default.fileExists(atPath: fileURL.path) else { return reject("E_FILE", "Video dosyası bulunamadı", nil) } // İzin İste PHPhotoLibrary.requestAuthorization { status in guard status == .authorized || status == .limited else { return reject("E_PERM", "Galeri izni verilmedi", nil) } // İşlemi Başlat self.generateLivePhoto(from: fileURL) { (pairedImageURL, pairedVideoURL) in guard let imageURL = pairedImageURL, let videoURL = pairedVideoURL else { return reject("E_GEN", "Live Photo oluşturulamadı", nil) } // Galeriye Kaydet self.saveToLibrary(imageURL: imageURL, videoURL: videoURL, albumName: albumName, resolve: resolve, reject: reject) } } } // MARK: - Generation Logic (Based on GitHub Reference) private func generateLivePhoto(from videoURL: URL, completion: @escaping (URL?, URL?) -> Void) { let assetIdentifier = UUID().uuidString let cacheDirectory = FileManager.default.temporaryDirectory // 1. Videodan Kapak Resmi Üret (Tam ortasından - 0.5) guard let keyPhotoURL = self.generateKeyPhoto(from: videoURL, atPercent: 0.5) else { print("Kapak resmi üretilemedi") completion(nil, nil) return } // 2. Resme Metadata Ekle let finalImageURL = cacheDirectory.appendingPathComponent(assetIdentifier).appendingPathExtension("jpg") guard let savedImageURL = self.addAssetID(assetIdentifier, toImage: keyPhotoURL, saveTo: finalImageURL) else { completion(nil, nil) return } // 3. Videoya Metadata ve Still Image Time Ekle let finalVideoURL = cacheDirectory.appendingPathComponent(assetIdentifier).appendingPathExtension("mov") self.addAssetID(assetIdentifier, toVideo: videoURL, saveTo: finalVideoURL) { (outputURL) in completion(savedImageURL, outputURL) } } // MARK: - Image Helpers private func generateKeyPhoto(from videoURL: URL, atPercent percent: Float) -> URL? { let asset = AVURLAsset(url: videoURL) let imageGenerator = AVAssetImageGenerator(asset: asset) imageGenerator.appliesPreferredTrackTransform = true imageGenerator.requestedTimeToleranceBefore = .zero imageGenerator.requestedTimeToleranceAfter = .zero let time = asset.duration let targetTimeValue = Int64(Float(time.value) * percent) let targetTime = CMTimeMake(value: targetTimeValue, timescale: time.timescale) do { let cgImage = try imageGenerator.copyCGImage(at: targetTime, actualTime: nil) let image = UIImage(cgImage: cgImage) let tempURL = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString).appendingPathExtension("jpg") if let data = image.jpegData(compressionQuality: 1.0) { try data.write(to: tempURL) return tempURL } } catch { print("Frame yakalama hatası: \(error)") } return nil } private func addAssetID(_ assetIdentifier: String, toImage imageURL: URL, saveTo destinationURL: URL) -> URL? { guard let imageDestination = CGImageDestinationCreateWithURL(destinationURL as CFURL, kUTTypeJPEG, 1, nil), let imageSource = CGImageSourceCreateWithURL(imageURL as CFURL, nil), var imageProperties = CGImageSourceCopyPropertiesAtIndex(imageSource, 0, nil) as? [AnyHashable : Any] else { return nil } let makerNote = ["17": assetIdentifier] imageProperties[kCGImagePropertyMakerAppleDictionary] = makerNote CGImageDestinationAddImageFromSource(imageDestination, imageSource, 0, imageProperties as CFDictionary) CGImageDestinationFinalize(imageDestination) return destinationURL } // MARK: - Video Helpers (The Complex Part) private func addAssetID(_ assetIdentifier: String, toVideo videoURL: URL, saveTo destinationURL: URL, completion: @escaping (URL?) -> Void) { let videoAsset = AVURLAsset(url: videoURL) guard let videoTrack = videoAsset.tracks(withMediaType: .video).first else { return completion(nil) } do { let assetWriter = try AVAssetWriter(outputURL: destinationURL, fileType: .mov) let videoReader = try AVAssetReader(asset: videoAsset) // Reader Output let readerOutputSettings = [kCVPixelBufferPixelFormatTypeKey as String: NSNumber(value: kCVPixelFormatType_32BGRA)] let videoReaderOutput = AVAssetReaderTrackOutput(track: videoTrack, outputSettings: readerOutputSettings) videoReader.add(videoReaderOutput) // Writer Input let writerOutputSettings: [String: Any] = [ AVVideoCodecKey: AVVideoCodecType.h264, AVVideoWidthKey: videoTrack.naturalSize.width, AVVideoHeightKey: videoTrack.naturalSize.height, ] let videoWriterInput = AVAssetWriterInput(mediaType: .video, outputSettings: writerOutputSettings) videoWriterInput.transform = videoTrack.preferredTransform videoWriterInput.expectsMediaDataInRealTime = true assetWriter.add(videoWriterInput) // Metadata: Asset ID let idItem = AVMutableMetadataItem() idItem.key = "com.apple.quicktime.content.identifier" as (NSCopying & NSObjectProtocol) idItem.keySpace = AVMetadataKeySpace.quickTimeMetadata idItem.value = assetIdentifier as (NSCopying & NSObjectProtocol) idItem.dataType = "com.apple.metadata.datatype.UTF-8" assetWriter.metadata = [idItem] // Metadata: Still Image Time (KRİTİK KISIM) let spec: NSDictionary = [ kCMMetadataFormatDescriptionMetadataSpecificationKey_Identifier as NSString: "mdta/com.apple.quicktime.still-image-time", kCMMetadataFormatDescriptionMetadataSpecificationKey_DataType as NSString: "com.apple.metadata.datatype.int8" ] var desc: CMFormatDescription? = nil CMMetadataFormatDescriptionCreateWithMetadataSpecifications(allocator: kCFAllocatorDefault, metadataType: kCMMetadataFormatType_Boxed, metadataSpecifications: [spec] as CFArray, formatDescriptionOut: &desc) let metaInput = AVAssetWriterInput(mediaType: .metadata, outputSettings: nil, sourceFormatHint: desc) let adaptor = AVAssetWriterInputMetadataAdaptor(assetWriterInput: metaInput) assetWriter.add(metaInput) // Start assetWriter.startWriting() videoReader.startReading() assetWriter.startSession(atSourceTime: .zero) // Still Image Time Verisini Yaz let stillTimePercent: Float = 0.5 let duration = videoAsset.duration let frameCount = Int(CMTimeGetSeconds(duration) * Float64(videoTrack.nominalFrameRate)) let frameDuration = Int64(Float(duration.value) / Float(frameCount)) let targetTimeValue = Int64(Float(duration.value) * stillTimePercent) let stillTime = CMTimeMake(value: targetTimeValue, timescale: duration.timescale) let stillTimeRange = CMTimeRangeMake(start: stillTime, duration: CMTimeMake(value: frameDuration, timescale: duration.timescale)) let stillItem = AVMutableMetadataItem() stillItem.key = "com.apple.quicktime.still-image-time" as (NSCopying & NSObjectProtocol) stillItem.keySpace = AVMetadataKeySpace.quickTimeMetadata stillItem.value = 0 as (NSCopying & NSObjectProtocol) stillItem.dataType = "com.apple.metadata.datatype.int8" let timedGroup = AVTimedMetadataGroup(items: [stillItem], timeRange: stillTimeRange) adaptor.append(timedGroup) // Video Yazma Döngüsü let queue = DispatchQueue(label: "rwQueue") videoWriterInput.requestMediaDataWhenReady(on: queue) { while videoWriterInput.isReadyForMoreMediaData { if let buffer = videoReaderOutput.copyNextSampleBuffer() { videoWriterInput.append(buffer) } else { videoWriterInput.markAsFinished() metaInput.markAsFinished() assetWriter.finishWriting { DispatchQueue.main.async { if assetWriter.status == .completed { completion(destinationURL) } else { print("Writer error: \(String(describing: assetWriter.error))") completion(nil) } } } videoReader.cancelReading() break } } } } catch { print("Setup hatası: \(error)") completion(nil) } } // MARK: - Save to Library private func saveToLibrary(imageURL: URL, videoURL: URL, albumName: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { PHPhotoLibrary.shared().performChanges({ let req = PHAssetCreationRequest.forAsset() let opts = PHAssetResourceCreationOptions() opts.shouldMoveFile = true req.addResource(with: .photo, fileURL: imageURL, options: opts) req.addResource(with: .pairedVideo, fileURL: videoURL, options: opts) }) { success, error in if success { resolve("Live Photo Saved!") } else { reject("E_SAVE", error?.localizedDescription ?? "Bilinmeyen hata", nil) } } } }
Read Entire Article