移植 msext DouyinShareManager.m:201-269 shareImageToDouyin 完整流程到 Swift。
新增基础设施(Phase 4.F 截图 / 远端图也共用):
Source/Share/ImageProvider.swift:
- captureScreenshot() throws -> UIImage:UIGraphicsImageRenderer 截
keyWindow(含状态栏 + letterbox 黑边 + WebView),与 msext
FuncPublic.getImageWithFullScreenshot 等价
- downloadRemote(_:) async throws -> UIImage:URLSession.data(from:)
下载远端 URL → UIImage 解码
- ImageProviderError 错误分类(noKeyWindow / downloadFailed / decodeFailed /
badURL)
Source/Share/PhotoLibrarySaver.swift:
- save(_:) async throws:使用 PHAccessLevel.addOnly(iOS 14+ 推荐,仅写
相册不读,弹窗更友好)+ PHAssetChangeRequest.creationRequestForAsset
- 自动处理权限三态(authorized/notDetermined 请求 + denied/restricted 抛错)
DouyinShare.share 完整实现:
- 拿图片源:
* type == "2" 截图 / type == "1" 链接(退化为当前屏截屏,msext 行为)
→ ImageProvider.captureScreenshot
* type == 其它(远端图 URL)→ ImageProvider.downloadRemote(webpageUrl)
- PhotoLibrarySaver.save 保存到相册(含 .permissionDenied 错误回 .failed)
- UIPasteboard.general.image = image 兜底(抖音可能从剪贴板读图)
- 按 msext 顺序尝试 4 个 URL Scheme:
snssdk1128://camera / publish / share/image / 基础 scheme
canOpenURL 命中即 await UIApplication.open + 调起即 success
- 全部失败返回 .failed("所有抖音 URL Scheme 都调起失败")
Info.plist 加 NSPhotoLibraryAddUsageDescription("需要将分享内容保存到
相册,以便您在抖音中选取分享"),addOnly 权限弹窗用此文案。
BuildProject 通过
Plan §5 Phase 4.4 勾选
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
60 lines
1.9 KiB
Swift
60 lines
1.9 KiB
Swift
//
|
|
// ImageProvider.swift
|
|
// ylgamehall
|
|
//
|
|
// 分享场景的图片源 helper:
|
|
// - captureScreenshot() 截当前 keyWindow(与 msext FuncPublic
|
|
// getImageWithFullScreenshot 等价)
|
|
// - downloadRemote(_:) URLSession 异步下载远端图片 URL
|
|
//
|
|
// Phase 4.F 截图 / 远端图分享共用。
|
|
//
|
|
|
|
import UIKit
|
|
|
|
public enum ImageProviderError: Error, Sendable {
|
|
case noKeyWindow
|
|
case downloadFailed(any Error)
|
|
case decodeFailed
|
|
case badURL(String)
|
|
}
|
|
|
|
@MainActor
|
|
public enum ImageProvider {
|
|
|
|
/// 截当前 keyWindow(与 msext `getImageWithFullScreenshot` 等价)。
|
|
/// 返回包含状态栏 + 大厅 letterbox 黑边 + WebView 内容的完整快照。
|
|
public static func captureScreenshot() throws -> UIImage {
|
|
guard let window = UIApplication.shared.connectedScenes
|
|
.compactMap({ $0 as? UIWindowScene })
|
|
.flatMap(\.windows)
|
|
.first(where: { $0.isKeyWindow })
|
|
else {
|
|
throw ImageProviderError.noKeyWindow
|
|
}
|
|
|
|
let renderer = UIGraphicsImageRenderer(bounds: window.bounds)
|
|
return renderer.image { _ in
|
|
window.drawHierarchy(in: window.bounds, afterScreenUpdates: false)
|
|
}
|
|
}
|
|
|
|
/// 下载远端 URL → UIImage。仅简单 URLSession 同步路径,无重试 / 缓存。
|
|
public static func downloadRemote(_ urlString: String) async throws -> UIImage {
|
|
guard let url = URL(string: urlString) else {
|
|
throw ImageProviderError.badURL(urlString)
|
|
}
|
|
do {
|
|
let (data, _) = try await URLSession.shared.data(from: url)
|
|
guard let image = UIImage(data: data) else {
|
|
throw ImageProviderError.decodeFailed
|
|
}
|
|
return image
|
|
} catch let err as ImageProviderError {
|
|
throw err
|
|
} catch {
|
|
throw ImageProviderError.downloadFailed(error)
|
|
}
|
|
}
|
|
}
|