一些基本沒用的有趣研究 [小專欄文章轉移]
本文轉移自本人小專欄的老文: 一些基本沒用的有趣研究 - 小專欄
最近研究了一些 iOS 11 ~ 12 上面的 Private Frameworks,最主要的目的是造一些比較有用的工具自己用。大家都知道,有私有呼叫的 app 是很容易被拒的,所以本文討論的範圍僅限於自娛自樂。
AvatarKit.framework
這個框架網上也有一些人研究,做的最好的是 AnimojiStudio ,簡單說這個框架是 iPhone X 上面 Animoji 的基礎,你可以通過呼叫他裡面的一些 API 來實現脫離 iMessage 使用 Animoji,並且能實現錄製和分享功能。
其實 AvatarKit 的介面暴露的很徹底,你只需要瞭解他有個 AVTPuppetView
就能實現大部分的功能了,這個檔案可以在這裡找到: AVTPuppetView.h 。這個類繼承自 AVTRecordView
,妙就妙在這裡, AVTRecordView
自己實現了一套錄製流程,你只需要實現這麼幾個方法:
- (void)startRecording; - (void)stopRecording; - (void)audioPlayerItemDidReachEnd:(id)arg1; - (bool)exportMovieToURL:(id)arg1 options:(id)arg2 completionHandler:(id /* block */)arg3;
其中 export
開頭的這個方法可以實現將視訊匯出到一個 URL,進而實現分享功能。
需要注意的是這個錄製預設只支援最多 30s 的時長,如果你想實現更長的錄製時間也是有辦法的,方法一是 Hack 下面幾個引數:
_recordingCapacity _rawTimesData _rawBlendShapesData _rawTransformsData
具體怎麼用可以網上找一下,這不是很聰明的辦法,更聰明的辦法是直接用ReplayKit,這是一個 Public Framework,任何應用都可以用 ReplayKit 錄製應用內的影象並匯出檔案。
下面是我在 JSBox 上面實現了這套 API 的使用效果:

另外,這裡有一篇非常有趣的文章,介紹瞭如何將 Memoji 移植到 iPad 的 Playgrounds 應用上: Create Memoji on iPad with Swift Playgrounds ,非常值得一讀。
MarkupUI.framework
大家都知道現在 iOS 內建了一個非常好的圖片標註工具,這個工具會在你截圖之後自動提示,或者在照片應用裡面通過圖片編輯擴充套件呼叫。我一直就在想,做的這麼好的東西如果能用在自己的指令碼上豈不是很方便...
遺憾的是這個 Framework 在網上並沒有什麼人討論,我只能自己挖 Runtime Headers,挖了一陣子終於找到了: MarkupViewController.h ,這個東西用起來比 AvatarKit 還簡單,就是一個 UIViewController,你需要呼叫的介面基本上就是:
- (void)setImage:(id)arg1; - (void)setShowShareButtonInToolbar:(bool)arg1;
當然了,你直接 present 這個 ViewController 的話可能會發現一些 UI 上的小問題,你可能需要修改這幾個 View 的 tintColor
: "_shareButton", "_shapesPickerButton", "_attributesPickerButton", "_currentColorButton"
,同時需要重寫這兩個方法來實現分享編輯後的圖片:
- (void)_toolbarShareButtonTapped:(id)arg1; - (bool)writeToURL:(id)arg1 error:(id*)arg2;
同樣的,分享一下在 JSBox 上實現了這套介面的效果:

DocumentCamera.framework
這其實是我最喜歡的一個,也花了多一點的時間去搞清楚 How it works,這個 Framework 就是備忘錄應用裡面那個超級強大的文件掃描工具,我可以毫不誇張地說這個實現比很多第三方付費的還要好。
但遺憾的是,目前而言在 iOS 裡面你依然只能在備忘錄裡面用:

沒用過的朋友可以試一下,總之就是一個 強 字。
同樣的,這個 Framework 網上也沒有討論,首先我看了一下 iOS 12 新增了哪些 Private Frameworks(有刪減):
... - /System/Library/PrivateFrameworks/AppPredictionUI.framework/AppPredictionUI - /System/Library/PrivateFrameworks/AppServerSupport.framework/AppServerSupport - /System/Library/PrivateFrameworks/AppleCV3DModels.framework/AppleCV3DModels - /System/Library/PrivateFrameworks/AppleMediaServices.framework/AppleMediaServices - /System/Library/PrivateFrameworks/AssetViewer.framework/AssetViewer - /System/Library/PrivateFrameworks/AvatarKit.framework/AvatarKit - /System/Library/PrivateFrameworks/AvatarUI.framework/AvatarUI - /System/Library/PrivateFrameworks/BookDataStore.framework/BookDataStore - /System/Library/PrivateFrameworks/C2.framework/C2 - /System/Library/PrivateFrameworks/CameraEffectsKit.framework/CameraEffectsKit - /System/Library/PrivateFrameworks/CarPlaySupport.framework/CarPlaySupport - /System/Library/PrivateFrameworks/CardServices.framework/CardServices - /System/Library/PrivateFrameworks/Categories.framework/Categories - /System/Library/PrivateFrameworks/ConversationKit.framework/ConversationKit - /System/Library/PrivateFrameworks/DoNotDisturb.framework/DoNotDisturb - /System/Library/PrivateFrameworks/DoNotDisturbKit.framework/DoNotDisturbKit - /System/Library/PrivateFrameworks/DoNotDisturbServer.framework/DoNotDisturbServer - /System/Library/PrivateFrameworks/DocumentCamera.framework/DocumentCamera - /System/Library/PrivateFrameworks/DocumentManager.framework/DocumentManager ...
那當然一個一個看下來的話最可疑的就是 DocumentCamera.framework
,所以我花了點時間瀏覽了下這個 Framework 下面有哪些有趣的類: DocumentCamera.framework 。
這個列表裡面有很多相似的類,看起來每個都像我要的,但是最終我發現管用的只有 DCDocumentCameraViewController_InProcess
這一個,其他的都沒有效果。確認這一點之後就很容易了,寫個子類繼承他,實現下面這兩個介面就行了:
- (void)documentCameraControllerDidCancel:(id)arg1; - (void)documentCameraController:(id)arg1 didFinishWithDocInfoCollection:(id)arg2 imageCache:(id)arg3 warnUser:(bool)arg4;
在 didFinish 裡面你可以拿到這個類匯出的資料結構,我這邊也一併說一下好了,去看一下 DCScannedDocument
的 header,你需要用得到的 info 初始化這個類,這個 Document 物件可以獲取掃描結果有多少頁,以及每頁的圖片資料,匯出就可以了。
你依然會碰到一些 UI 上面的小問題,畢竟這個 Framework 不是設計這麼來用的,我的做法是 Hook 這個類: ICDocCamExtractedDocumentViewController
,把他裡面那幾個 UIBarButtonItem 的 tintColor 都給改對。
同樣的,下面展示下用 JSBox 實現這套 API 的效果:

MobileTimerUI.framework
系統的時鐘應用裡面,有個做的不錯的世界時鐘,通過研究這個 Framework,我把他整個介面給移植了出來,確切地說我做成了一個通知中心小元件:

這個小元件的資料可以完全和系統時鐘的同步,編輯系統時鐘,這裡也會同步的改變,幾乎就是一模一樣(為了適應通知中心,縮小了顯示尺寸)。
做這個效果需要了解兩個私有類: MTUIWorldClockCellView 和 WorldClockManager 。一個用來讀資料,一個用來顯示成檢視。
要做的事情很簡單,對 manager 呼叫 loadCities
得到一個城市列表,然後對每一個 city 獲取他的 timezone
,用這個來設定 MTUIWorldClockCellView 就行了。
BatteryCenter.framework
這個是一個小眾的需求,大家都知道在 iOS 的通知中心有個電池小元件,可以顯示連線上的所有裝置的電量以及充電狀態,這個功能就是通過 BatteryCenter.framework 實現的。
這個 Framework 暴露的介面極為簡單,用 BCBatteryDeviceController
獲取到 connectedDevices
,然後每個 device 的結構是一個 BCBatteryDevice
,用 Runtime 獲取他們的欄位就可以了,比如:
@property (nonatomic, copy) NSString *name; @property (nonatomic) long long percentCharge; @property (getter=isCharging, nonatomic) bool charging; @property (getter=isConnected, nonatomic) bool connected; @property (getter=isLowBattery, nonatomic) bool lowBattery;
名稱、百分比、是否充電等等資訊都有了。這個效果非常簡單,我就不展示效果了。
沒有太多實際用處,研究這個的來源是我有個使用者希望在他的腳本里面獲取 AirPods 的電量,用這個方法就可以做到。
如何呼叫這些介面
這不是本文要討論的問題,作為 iOS 開發你應該已經掌握了這些技能,如果沒有的話 You can Google it。
本文就是分享這麼一些無聊時候的研究,做點自己有用的工具,請千萬不要用在生產環境。
這種事情就像挖寶一樣,你根本不知道會挖到什麼,有可能你挖了一整天卻什麼都沒有,只有你自己去挖一挖才能明白其中的樂趣。
點到為止吧,再見。