swift 制作动态壁纸(live)实况图

news/2024/11/23 3:16:25/

前言

我相信老铁们都是走在时代前沿的弄潮儿,那么为你们的爱机自定义一张动态壁纸,我想这应该是一个 Good idea😎,如下图便是我制作的动态壁纸。

制作动态壁纸

动态壁纸在iOS中其实就是用实况图设置锁屏壁纸,在锁频界面长按就会播放视频内容

什么是实况图?

  • 百度释义:

苹果实况照片的意思是指动态照片Live Photos,它的作用是在照片拍摄前后录制一段1.5秒的“动态视频”,当用户在照片上深按一下,照片就会自动播放动态效果。

  • 程序员视角:

一张jpg图片作为封面 + .mov 视频

如何制作实况图?

  • 思路:

准备一张封面和一段.mov格式的视频,分别写入identifier,然后保存到系统相册,相册会通过identifier将它们绑定起来便成了我们看到的实况图

  • 第一步 图片写入identifier

 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),let imageRef = CGImageSourceCreateImageAtIndex(imageSource, 0, nil),var imageProperties = CGImageSourceCopyPropertiesAtIndex(imageSource, 0, nil) as? [AnyHashable : Any] else { return nil }let assetIdentifierKey = "17"let assetIdentifierInfo = [assetIdentifierKey : assetIdentifier]imageProperties[kCGImagePropertyMakerAppleDictionary] = assetIdentifierInfoCGImageDestinationAddImage(imageDestination, imageRef, imageProperties as CFDictionary)CGImageDestinationFinalize(imageDestination)return destinationURL}

  • 第二步 视频写入 identifier并且 输出.mov格式

   var audioReader: AVAssetReader?var videoReader: AVAssetReader?var assetWriter: AVAssetWriter?func addAssetID(_ assetIdentifier: String, toVideo videoURL: URL, saveTo destinationURL: URL, progress: @escaping (CGFloat) -> Void, completion: @escaping (URL?) -> Void) {var audioWriterInput: AVAssetWriterInput?var audioReaderOutput: AVAssetReaderOutput?let videoAsset = AVURLAsset(url: videoURL)let frameCount = videoAsset.countFrames(exact: false)guard let videoTrack = videoAsset.tracks(withMediaType: .video).first else {completion(nil)return}do {// Create the Asset WriterassetWriter = try AVAssetWriter(outputURL: destinationURL, fileType: .mov)// Create Video Reader OutputvideoReader = try AVAssetReader(asset: videoAsset)let videoReaderSettings = [kCVPixelBufferPixelFormatTypeKey as String: NSNumber(value: kCVPixelFormatType_32BGRA as UInt32)]let videoReaderOutput = AVAssetReaderTrackOutput(track: videoTrack, outputSettings: videoReaderSettings)videoReader?.add(videoReaderOutput)// Create Video Writer Inputlet videoWriterInput = AVAssetWriterInput(mediaType: .video, outputSettings: [AVVideoCodecKey : AVVideoCodecH264, AVVideoWidthKey : videoTrack.naturalSize.width, AVVideoHeightKey : videoTrack.naturalSize.height])videoWriterInput.transform = videoTrack.preferredTransformvideoWriterInput.expectsMediaDataInRealTime = trueassetWriter?.add(videoWriterInput)// Create Audio Reader Output & Writer Inputif let audioTrack = videoAsset.tracks(withMediaType: .audio).first {do {let _audioReader = try AVAssetReader(asset: videoAsset)let _audioReaderOutput = AVAssetReaderTrackOutput(track: audioTrack, outputSettings: nil)_audioReader.add(_audioReaderOutput)audioReader = _audioReaderaudioReaderOutput = _audioReaderOutputlet _audioWriterInput = AVAssetWriterInput(mediaType: .audio, outputSettings: nil)_audioWriterInput.expectsMediaDataInRealTime = falseassetWriter?.add(_audioWriterInput)audioWriterInput = _audioWriterInput} catch {print(error)}}// Create necessary identifier metadata and still image time metadatalet assetIdentifierMetadata = metadataForAssetID(assetIdentifier)let stillImageTimeMetadataAdapter = createMetadataAdaptorForStillImageTime()assetWriter?.metadata = [assetIdentifierMetadata]assetWriter?.add(stillImageTimeMetadataAdapter.assetWriterInput)// Start the Asset WriterassetWriter?.startWriting()assetWriter?.startSession(atSourceTime: CMTime.zero)// Add still image metadatalet _stillImagePercent: Float = 0.5stillImageTimeMetadataAdapter.append(AVTimedMetadataGroup(items: [metadataItemForStillImageTime()],timeRange: videoAsset.makeStillImageTimeRange(percent: _stillImagePercent, inFrameCount: frameCount)))// For end of writing / progressvar writingVideoFinished = falsevar writingAudioFinished = falsevar currentFrameCount = 0func didCompleteWriting() {guard writingAudioFinished && writingVideoFinished else { return }assetWriter?.finishWriting {if self.assetWriter?.status == .completed {completion(destinationURL)} else {completion(nil)}}}// Start writing videoif videoReader?.startReading() ?? false {videoWriterInput.requestMediaDataWhenReady(on: DispatchQueue(label: "videoWriterInputQueue")) {while videoWriterInput.isReadyForMoreMediaData {if let sampleBuffer = videoReaderOutput.copyNextSampleBuffer()  {currentFrameCount += 1let percent:CGFloat = CGFloat(currentFrameCount)/CGFloat(frameCount)progress(percent)if !videoWriterInput.append(sampleBuffer) {print("Cannot write: \\(String(describing: self.assetWriter?.error?.localizedDescription))")self.videoReader?.cancelReading()}} else {videoWriterInput.markAsFinished()writingVideoFinished = truedidCompleteWriting()}}}} else {writingVideoFinished = truedidCompleteWriting()}// Start writing audioif audioReader?.startReading() ?? false {audioWriterInput?.requestMediaDataWhenReady(on: DispatchQueue(label: "audioWriterInputQueue")) {while audioWriterInput?.isReadyForMoreMediaData ?? false {guard let sampleBuffer = audioReaderOutput?.copyNextSampleBuffer() else {audioWriterInput?.markAsFinished()writingAudioFinished = truedidCompleteWriting()return}audioWriterInput?.append(sampleBuffer)}}} else {writingAudioFinished = truedidCompleteWriting()}} catch {print(error)completion(nil)}}private func metadataForAssetID(_ assetIdentifier: String) -> AVMetadataItem {let item = AVMutableMetadataItem()let keyContentIdentifier =  "com.apple.quicktime.content.identifier"let keySpaceQuickTimeMetadata = "mdta"item.key = keyContentIdentifier as (NSCopying & NSObjectProtocol)?item.keySpace = AVMetadataKeySpace(rawValue: keySpaceQuickTimeMetadata)item.value = assetIdentifier as (NSCopying & NSObjectProtocol)?item.dataType = "com.apple.metadata.datatype.UTF-8"return item}private func createMetadataAdaptorForStillImageTime() -> AVAssetWriterInputMetadataAdaptor {let keyStillImageTime = "com.apple.quicktime.still-image-time"let keySpaceQuickTimeMetadata = "mdta"let spec : NSDictionary = [kCMMetadataFormatDescriptionMetadataSpecificationKey_Identifier as NSString:"\\(keySpaceQuickTimeMetadata)/\\(keyStillImageTime)",kCMMetadataFormatDescriptionMetadataSpecificationKey_DataType as NSString:"com.apple.metadata.datatype.int8"            ]var desc : CMFormatDescription? = nilCMMetadataFormatDescriptionCreateWithMetadataSpecifications(allocator: kCFAllocatorDefault, metadataType: kCMMetadataFormatType_Boxed, metadataSpecifications: [spec] as CFArray, formatDescriptionOut: &desc)let input = AVAssetWriterInput(mediaType: .metadata,outputSettings: nil, sourceFormatHint: desc)return AVAssetWriterInputMetadataAdaptor(assetWriterInput: input)}private func metadataItemForStillImageTime() -> AVMetadataItem {let item = AVMutableMetadataItem()let keyStillImageTime = "com.apple.quicktime.still-image-time"let keySpaceQuickTimeMetadata = "mdta"item.key = keyStillImageTime as (NSCopying & NSObjectProtocol)?item.keySpace = AVMetadataKeySpace(rawValue: keySpaceQuickTimeMetadata)item.value = 0 as (NSCopying & NSObjectProtocol)?item.dataType = "com.apple.metadata.datatype.int8"return item}
  • 最后 保存实况图到相册


typealias LivePhotoResources = (pairedImage: URL, pairedVideo: URL)/// Save a Live Photo to the Photo Library by passing the paired image and video.public class func saveToLibrary(_ resources: LivePhotoResources, completion: @escaping (Bool) -> Void) {PHPhotoLibrary.shared().performChanges({let creationRequest = PHAssetCreationRequest.forAsset()let options = PHAssetResourceCreationOptions()creationRequest.addResource(with: PHAssetResourceType.pairedVideo, fileURL: resources.pairedVideo, options: options)creationRequest.addResource(with: PHAssetResourceType.photo, fileURL: resources.pairedImage, options: options)}, completionHandler: { (success, error) inif error != nil {print(error as Any)}completion(success)})}

看到这里,相信你已经可以制作出实况图了,快去让你的屏幕炫起来吧!

😂看大段代码确实打脑壳,那就来个demo吧!

SwiftLivePhoto-demo


如果觉得文章对你有用,那就点个赞支持一下吧!如果有任何疑问或写得不好的地方欢迎在评论区留言 🙏


http://www.ppmy.cn/news/672841.html

相关文章

live2dviewer android,Live2DViewerEX.apk

Live2DViewerEX.apk是一款可以改变桌布的软件,为您添加一个动态桌布背景,有性价比的设置软件,目前主要的卖点还是有丰富的模型素材,可以让用户一键设置自己的桌面,改变以前无趣的桌面。 软件特色 1.支持跨平台使用&…

android动态壁纸LiveWallpaper用法

原文地址&#xff1a;http://blog.csdn.net/oldmtn/article/details/9177123 学习到的知识总结&#xff1a; 先贴上我的AndroidManifest.xml的内容。 [html] view plain copy print? <?xml version"1.0" encoding"utf-8"?> <manifest xmlns…

Android Live Wallpaper

Android Live Wallpaper&#xff0c;这篇教程讲解android透明屏幕的实现方式。它以eclipse4.2、java 1.6、android4.1位基础。 1. 提前预习 下面的教程假设您已经掌握了android开发的基本知识 Android development。Android开发教程&#xff0c;学习基本知识&#xff0c;可以参…

live java viewer_live2dviewerex

live2dviewerex动态桌面官网版是一款非常有趣的手机动态桌布软件&#xff0c;通过这款live2dviewerex&#xff0c;用户可以在自己的手机桌面上生成一个自己喜欢的动漫角色&#xff0c;这个角色不仅会动&#xff0c;而且还可以进行互动&#xff0c;是一款非常有趣的软件&#xf…

腾讯云国际版云服务器按量计费关机不收费是怎么回事呢?

实例关机不收费是指按量计费实例通过选择关机不收费选项使实例进入已关机状态后&#xff0c;不再收取实例&#xff08;CPU、内存&#xff09;费用。云盘&#xff08;系统盘和数据盘&#xff09;、和镜像等组件将继续计费。 本文介绍按量计费实例关机不收费说明&#xff0c;下面…

ECharts 横坐标倾斜设置的实现方法详解

系列文章目录 文章目录 系列文章目录前言一、使用 rotate 属性调整横坐标标签倾斜角度二、使用 formatter 属性自定义横坐标标签显示内容总结 前言 在使用 ECharts 进行数据可视化时&#xff0c;有时候需要调整横坐标的显示方式&#xff0c;使其倾斜以适应较长的标签或增加可读…

途乐证券|沪指涨0.78%收复3200点,券商、银行等板块拉升

30日早盘&#xff0c;两市股指盘中全线走高&#xff0c;沪指在券商、银行等板块的带动下收复3200点&#xff0c;创业板指大涨2%&#xff1b;两市半日成交约5500亿元&#xff0c;北向资金净买入超50亿元。 截至午间收盘&#xff0c;沪指涨0.78%报3207.27点&#xff0c;深成指涨1…

《MySQL》什么是数据库

文章目录 数据库的理解MySQL的架构SQL语句分类存储引擎 数据库的理解 我们所下的数据库软件(如MySQL)其实是中间件。如何理解呢&#xff0c;如图&#xff1a; 我们用户通过MySQL对数据进行管理。 mysql #客户端程序 mysqld #服务端程序&#xff0c;再修改配置后需要重新启动…