1. 首先先看使用效果

如果你不是開(kāi)發(fā)者,只是想使用這個(gè)app,可以點(diǎn)擊下方鏈接下載安裝使用。核心功能都是免費(fèi)的,可以放心食用。 如果鏈接不能直接跳轉(zhuǎn),選擇在Safari中打開(kāi)
iPhone需要iOS14及以上版本才支持畫(huà)中畫(huà)功能。
也可以直接在App Store搜索:懸浮秒殺助手,第一個(gè)app就是
2. 開(kāi)始之前
- 首先
iOS決定了開(kāi)發(fā)者沒(méi)法為用戶(hù)提供一個(gè)系統(tǒng)級(jí)別的view,也就是不能實(shí)現(xiàn)所謂的懸浮窗等功能。 所以開(kāi)發(fā)者只能AVPictureInPictureController來(lái)實(shí)現(xiàn)類(lèi)似的效果。 -
AVPictureInPictureController一般用于實(shí)現(xiàn)視頻畫(huà)中畫(huà)播放功能。 因此要想實(shí)現(xiàn)時(shí)鐘的效果, 這個(gè)問(wèn)題就變成了制作一個(gè)播放時(shí)間的視頻,然后進(jìn)行播放。
這里需要思考一個(gè)問(wèn)題 : 如何保持視頻時(shí)間和系統(tǒng)時(shí)間的同步,讓用戶(hù)
暫停快進(jìn)操作不影響到時(shí)間變化
這個(gè)問(wèn)題是這個(gè)功能的實(shí)際難點(diǎn), 你肯定不希望這個(gè)時(shí)間有誤差或者是受用戶(hù)影響(比如用戶(hù)暫停了視頻播放,時(shí)間就不動(dòng)了)
3. 實(shí)現(xiàn)思路
- 首先想到的肯定是制作一段視頻,視頻內(nèi)容就是時(shí)間流, 速率和真實(shí)時(shí)間一致。在用戶(hù)操作后進(jìn)行播放。 實(shí)際上這個(gè)方案行不通,因?yàn)橛脩?hù)很容易通過(guò)暫??爝M(jìn)等操作導(dǎo)致時(shí)間不同步
- 如果只是在App內(nèi)部顯示當(dāng)前時(shí)間,我們只需要通過(guò)
UILabel和定時(shí)器就很容易的進(jìn)行實(shí)現(xiàn)。 如果能在AVPictureInPictureController控制的圖層上添加視圖,也就能很方便的達(dá)到我們的效果。 但是經(jīng)過(guò)實(shí)際研究, 雖然可以通過(guò)比較hack的方式在畫(huà)中畫(huà)上添加view,但是會(huì)有很多問(wèn)題,比如用戶(hù)縮放,view并不會(huì)隨之縮放。效果比較差
以上兩種方案是最初的思路,都沒(méi)能很好的達(dá)到目的。 最終我采用了視頻實(shí)時(shí)渲染的方案,當(dāng)用戶(hù)開(kāi)始使用
畫(huà)中畫(huà),我們實(shí)時(shí)渲染當(dāng)前時(shí)間。并且我們?cè)谝曨l停止播放的時(shí)候,也要刷新屏幕時(shí)間。
這樣,呈現(xiàn)給用戶(hù)的就是一個(gè)隨著系統(tǒng)時(shí)間實(shí)時(shí)刷新的懸浮窗口了
思路有了,你可以通過(guò)下面的鏈接,把工程下載到本地嘗試一下。需要注意的是iPhone在iOS14 才開(kāi)始支持畫(huà)中畫(huà)功能。
如果你使用模擬器測(cè)試,可以使用iPad的模擬器運(yùn)行項(xiàng)目,iphone的模擬器目前不支持畫(huà)中畫(huà)功能
4. 代碼說(shuō)明
1. 先看目錄結(jié)構(gòu),非常簡(jiǎn)單。ViewController 管理我們demo首頁(yè)的視圖和操作邏輯
我們會(huì)使用到temp.mov 作為視頻進(jìn)行畫(huà)中畫(huà)的呈現(xiàn),這個(gè)視頻非常短,本身也沒(méi)有任何內(nèi)容。我們所顯示的時(shí)鐘內(nèi)容全部是通過(guò)TimeVideoComposition 和TimeVideoCompositionInstruction 這兩個(gè)類(lèi)實(shí)現(xiàn)的 。后面會(huì)我們會(huì)看到在如何去使用它們

2. 然后我們進(jìn)入Viewcontroller 看一下對(duì)應(yīng)的方法。
首先配置UI和加載Video
override func viewDidLoad() {
super.viewDidLoad()
setupUI()
setupVideo()
}
等到視頻加載完成,我們調(diào)用
setupComposition方法進(jìn)行視頻合成的的配置。 簡(jiǎn)單來(lái)說(shuō),我們使用自己創(chuàng)建的TimeVideoComposition作為視屏的合成器,然后通過(guò)TimeVideoCompositionInstruction進(jìn)行視頻合成。
func setupComposition() {
// For best performance, ensure that the duration and tracks properties of the asset are already loaded before invoking this method.
videoComposition = AVMutableVideoComposition(propertiesOf: asset!)
let instructions = videoComposition.instructions as! [AVVideoCompositionInstruction]
var newInstructions: [AVVideoCompositionInstructionProtocol] = []
guard let instruction = instructions.first else {
return
}
let layerInstructions = instruction.layerInstructions
// TrackIDs
var trackIDs: [CMPersistentTrackID] = []
for layerInstruction in layerInstructions {
trackIDs.append(layerInstruction.trackID)
}
timeInstruction = TimeVideoCompositionInstruction(trackIDs as [NSValue], timeRange: instruction.timeRange)
timeInstruction.layerInstructions = layerInstructions
newInstructions.append(timeInstruction)
videoComposition.instructions = newInstructions
self.videoComposition?.customVideoCompositorClass = TimeVideoComposition.self
item?.videoComposition = videoComposition
}
想要深入了解的可以參考文末鏈接
3. 上一步我們使用到了TimeVideoCompositionInstruction,核心部分就是在這個(gè)類(lèi)中實(shí)現(xiàn)。當(dāng)視頻需要我們提供界面的時(shí)候, 我們?cè)?code>getPixelBuffer 提供對(duì)應(yīng)的界面。這個(gè)工程中我們使用CoreText 繪制了當(dāng)前時(shí)間
func getPixelBuffer(_ renderContext: AVVideoCompositionRenderContext) -> CVPixelBuffer? {
.......
}
看到這里,基本流程就已經(jīng)走通了。 用戶(hù)使用
畫(huà)中畫(huà)功能,視頻調(diào)用合成器進(jìn)行顯示內(nèi)容的處理,我們提供需要顯示的畫(huà)面
4. 在視頻加載完成以后,除了配置合成器,我們還開(kāi)啟了一個(gè)定時(shí)器。
DispatchQueue.main.async {
//配置合成器
self.setupComposition()
//創(chuàng)建定時(shí)器
self.createDisplayLink()
}
定時(shí)器開(kāi)啟后,我們每次都需要給AVPlayerItem 進(jìn)行賦值, 以此觸發(fā)視頻的刷新。 這樣不論用戶(hù)是否播放暫停視頻,我們都能在界面上顯示最新的內(nèi)容
@objc func refresh(displaylink: CADisplayLink) {
reloadTime()
item?.videoComposition = videoComposition
}
5. 小結(jié)
實(shí)際流程非常簡(jiǎn)單,但是實(shí)現(xiàn)寫(xiě)代碼的時(shí)候查閱了大量文檔視頻,用很多方式實(shí)現(xiàn)了這個(gè)功能,最終覺(jué)的這種方式效果最好。 其中有難點(diǎn)的就是理解
AVVideoCompositingAVVideoCompositionInstructionProtocol的使用, 對(duì)這部分有疑惑的小伙伴可以多參考官方的文檔和視頻。
同時(shí)可以下載我們的App體驗(yàn)一下(說(shuō)不定能搶個(gè)茅臺(tái) ( ̄▽?zhuān)?~*)
x. 一些參考鏈接
Building a Real-Time Video Editor with AVFoundation
Working with Media in AV Foundation