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

前言

我相信老铁们都是走在时代前沿的弄潮儿,那么为你们的爱机自定义一张动态壁纸,我想这应该是一个 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://xiahunao.cn/news/255597.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

android 时钟旋转动画,抖音上超火的时间轮盘时钟轮盘八卦太极动态壁纸软件分享安卓苹果都可以设置!...

原标题:抖音上超火的时间轮盘时钟轮盘八卦太极动态壁纸软件分享安卓苹果都可以设置! 最近抖音上面特别火的一个视频,时间轮盘壁纸,之前有给大家分享过安卓版本的,今天再给大家分享一个苹果版的; 安卓版&…

【Mac新手必看】Desktop Picture壁纸文件夹找不到怎么办?苹果壁纸设置教程

相信很多朋友像小编一样,喜欢设置各种有意思的桌面壁纸,不管是工作还是生活,每天对着自己喜欢的壁纸心情都超级棒呢~那这些Mac壁纸小技巧你们知道吗? 如何设置桌面壁纸? 你可以点击屏幕左上角的“苹果图标”-“系统偏…

动态壁纸安卓_抖音火爆的时间罗盘壁纸,苹果安卓都支持!

点击【抖音壁纸表情】- 右上角找到【…】 立刻设我为星标/置顶 - 谢谢你、 求图请加V:moonkiss0505 长按保存 点击放大 图片很大,请在 W i f i 下浏览 大家好,这两天大家的抖音是不是被这款时间轮盘壁纸给刷爆了啊,在抖音真是…

python实现动态壁纸_python学习笔记 | macOS Big Sur动态壁纸食用指南

目录 前言 北京时间23日凌晨1点,苹果WWDC2020大会开幕。在发布会上,苹果正式发布了新版macOS,并将其命名为“Big Sur”。 相比于外界争议最大的UI界面,令我更为关注的是这次的系统动态壁纸,跟过去几年的版本更新一样…

苹果5壁纸_冷高轮时间wallpaper Engine动态时钟壁纸 小人人体形状造型数字 手势数字 麻将数字 扑克数字 时钟壁纸...

冷高轮时间wallpaper Engine电脑动态时钟壁纸壁纸 小人人体形状造型数字 手势数字 麻将数字 扑克数字 动态时钟壁纸 1.需要在steam中下载wallpaper Engine 安装好wallpaper Engine后进入壁纸软件 1.点击创意工坊 2.搜索时钟全称(此时由于没选择标签可能指搜索出来一…

动态壁纸安卓_抖音上超火的时间轮盘动态壁纸,安卓苹果都可以设置!个性好看...

今天给大家分享一个最近很火的时间轮盘带姓氏的壁纸 效果呢就像我们现在看到的这样 看起来呢有点像古墓的感觉 同时我们常见的带姓氏的壁纸我也已经给大家准备好了 制作呢也非常的简单 我们只需要在微信的搜索页面搜索kx2687 点击搜一搜 点击进入 在下方的对话框中回复338 我们…

chatgpt赋能python:Python中的连接符:介绍与应用

Python中的连接符:介绍与应用 在Python编程中,连接符起着关键性的作用,它是连接不同代码部分的纽带。本篇文章将重点介绍几种常用的Python连接符。 一、加号连接符() 加号连接符最常见,用于连接不同的字…

JGJ107-2016 钢筋机械连接技术规程 免费下载

【资源介绍】 资源名称:JGJ107-2016 钢筋机械连接技术规程 资源分类: 建筑工程标准规范 其他简介:JGJ107-2016 钢筋机械连接技术规程 【资源下载】 链接:https://pan.baidu.com/s/1IZdTq2ga8V2psdek4UVQNA 提取码&#xff1…

钢筋的字体表示

1、把字体下载解压后,复制到控制面板的字体里面ok,打开word然后在开始\附件\系统工具\字符映射表打开然后在复制钢筋型号到word中就ok了2、先把字体COPY到windows/font文件夹里,在word中选用"SJQY"字体,然后点”插入/符号“里面有就钢筋的符号…

Revit:放置标高和轴网方法总结

本次系列教学中涉及到的建模方法在Revit2018和Revit2020均适用 首先使用快捷键“VV”,在“场地”下找到“项目基点”,该点表示在二维坐标系中的原点,坐标始终为(0,0),在三维坐标系同样表示原点…

通达信破底翻形态选股公式,选出破底之后再翻回的标的

破底翻形态,顾名思义就是跌破底部之后再翻回来。编写不带未来函数的形态类公式还是比较难的,理解也不容易。通过今天介绍的破底翻形态选股公式,希望能给大家一些思路。 一、FINDLOWBARS、FINDLOWBARS函数 1、FINDLOWBARS 含义:计…

LabVIEW2023中文版软件安装包、工具包、安装教程下载

下载链接:LabVIEW及工具包大全-三易电子工作室http://blog.eeecontrol.com/labview6666 《LabVIEW2023安装图文教程》 1、解压后,双击install.exe安装 2、选中“我接受上述许可协议”,点击下一步 3、点击下一步,安装NI Package …

如何拆分PDF,PDF拆分成多个PDF的方法

如何拆分PDF文件呢?我们想要将PDF文件拆分成多个PDF文件需要怎么操作呢?其实方法很简单,只不过需要使用到专业的PDF编辑器,下面小编就使用迅捷PDF编辑器为大家操作一下PDF拆分的方法。 操作软件:迅捷PDF编辑器 具体操作…

Acrobat如何将PDF拆分为多个文档

今天小e打算分享一个电子书,结果一上传准备发布外链的时候,居然超出了100M,只有会员才可以分享100M以上的文档或者软件,小e穷,没钱升会员,所以只能把这个完整的电子书拆分成两份,特把拆分的方法…

如何拆分PDF成单页?这三个方法分享给你

很多朋友在平时的工作中,经常需要处理一些PDF格式的文件,但是如果PDF文件的占用空间太大,难以进行操作处理,这时我们就需要先将其拆分成多个小文件,那你们知道要怎么把PDF拆分成多个文件吗?今天我就来给大家…

计算机存储介质清除工具,天桥科技存储介质信息消除工具

天桥科技存储介质信息消除工具是一款可清除存储介质信息的软件,此软件主要提供了数据粉碎、全盘粉碎、空间粉碎、痕迹清理、上网清除、介质清除等功能模块,可有效的帮助用户对电脑上或者U盘上的介质信息、存储数据等进行删除,相信现在的大多数电脑用户对于存储介质都是使用覆…

致《上网记录深度擦除工具》用户的说明

2014-08-27 12:00 补充更新 Blog好久不更新了,没想到两年前写的这个小程序使用的人还不少,但是一方面有不少人对这个工具的使用还存在一定误解,另一方面总有64位系统用户向我反应这个程序为什么在64位系统上无法使用?如何解决这个…

chatgpt赋能python:Python中的迭代器

Python中的迭代器 在Python中,迭代器是一种对象,它可以让我们可以遍历(或迭代)序列中的元素而不必了解它们如何存储在内存中。迭代器是Python中许多高级构造的基础 - 他们节省了空间,并且它们能够帮助我们更有效地处理…

chatgpt赋能python:Python中的转置操作:理解与实践

Python中的转置操作:理解与实践 在Python中,可以使用转置操作来将矩阵或数组的行和列交换位置。转置操作不仅在数学和统计学上有广泛应用,也在机器学习和数据分析领域中非常重要。在本文中,我们将深入探讨Python中的转置操作的基…

chatgpt赋能python:Python中语句太长之续行符的使用

Python中语句太长之续行符的使用 如果你是一位有10年Python编程经验的工程师,那么你一定会遭遇语句太长的问题。这是导致程序出错的常见问题。在很多情况下,一条语句的长度会超过Python规定的最大长度,这时候我们就需要使用续行符进行换行了…