fix(iOS): move pendingExportURL to PlayerBridge — SwiftUI can now observe it and trigger save sheet
This commit is contained in:
parent
3ed5696ae0
commit
1412c234f8
@ -10,7 +10,6 @@ import Photos
|
||||
final class HLSRecorder: ObservableObject {
|
||||
@Published var isRecording = false
|
||||
@Published var durationText = "00:00"
|
||||
@Published var pendingExportURL: URL?
|
||||
|
||||
var onRecordingSaved: ((URL) -> Void)?
|
||||
var onError: ((String) -> Void)?
|
||||
@ -42,7 +41,6 @@ final class HLSRecorder: ObservableObject {
|
||||
self.isRecording = false
|
||||
self.timer?.invalidate()
|
||||
self.onRecordingSaved?(outputURL)
|
||||
self.showExportSheet(url: outputURL)
|
||||
}
|
||||
} catch is CancellationError {
|
||||
NSLog("[MiniPlayer] Recording task cancelled")
|
||||
@ -294,108 +292,6 @@ final class HLSRecorder: ObservableObject {
|
||||
return outputMP4
|
||||
}
|
||||
|
||||
// MARK: - 导出/分享
|
||||
|
||||
private func showExportSheet(url: URL) {
|
||||
NSLog("[MiniPlayer] showExportSheet: \(url.path)")
|
||||
|
||||
// 1. 保存到相册(使用 PHPhotoLibrary,有回调通知结果)
|
||||
if url.pathExtension.lowercased() == "mp4" || url.pathExtension.lowercased() == "mov" {
|
||||
saveToPhotoLibrary(url: url)
|
||||
}
|
||||
|
||||
// 2. 通过 SwiftUI sheet 弹出分享面板(而非直接 present UIViewController)
|
||||
DispatchQueue.main.async { [weak self] in
|
||||
self?.pendingExportURL = url
|
||||
}
|
||||
}
|
||||
|
||||
private func saveToPhotoLibrary(url: URL) {
|
||||
let status = PHPhotoLibrary.authorizationStatus(for: .addOnly)
|
||||
|
||||
if status == .authorized || status == .limited {
|
||||
performSave(url: url)
|
||||
} else if status == .notDetermined {
|
||||
PHPhotoLibrary.requestAuthorization(for: .addOnly) { [weak self] newStatus in
|
||||
DispatchQueue.main.async {
|
||||
if newStatus == .authorized || newStatus == .limited {
|
||||
self?.performSave(url: url)
|
||||
} else {
|
||||
self?.onError?("Photo library access denied")
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
onError?("Photo library access denied. Please enable in Settings.")
|
||||
}
|
||||
}
|
||||
|
||||
private func performSave(url: URL) {
|
||||
PHPhotoLibrary.shared().performChanges {
|
||||
PHAssetCreationRequest.forAsset().addResource(with: .video, fileURL: url, options: nil)
|
||||
} completionHandler: { [weak self] success, error in
|
||||
DispatchQueue.main.async {
|
||||
if success {
|
||||
self?.onRecordingSaved?(url)
|
||||
NSLog("[MiniPlayer] Video saved to Photos")
|
||||
} else {
|
||||
self?.onError?("Save to Photos failed: \(error?.localizedDescription ?? "unknown")")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func presentActivitySheet(url: URL) {
|
||||
NSLog("[MiniPlayer] presentActivitySheet called for: %@", url.lastPathComponent)
|
||||
|
||||
guard let windowScene = UIApplication.shared.connectedScenes
|
||||
.compactMap({ $0 as? UIWindowScene })
|
||||
.first(where: { $0.activationState == .foregroundActive }) ?? UIApplication.shared.connectedScenes.compactMap({ $0 as? UIWindowScene }).first else {
|
||||
NSLog("[MiniPlayer] No window scene found")
|
||||
return
|
||||
}
|
||||
|
||||
// 找最顶层可见的 VC:先用 keyWindow,再 fallback 到任意 window
|
||||
var topVC: UIViewController?
|
||||
if let keyWindow = windowScene.keyWindow {
|
||||
topVC = keyWindow.rootViewController
|
||||
}
|
||||
if topVC == nil {
|
||||
for window in windowScene.windows where !window.isHidden && window.bounds.size != .zero {
|
||||
topVC = window.rootViewController
|
||||
if topVC != nil { break }
|
||||
}
|
||||
}
|
||||
if topVC == nil {
|
||||
topVC = windowScene.windows.first?.rootViewController
|
||||
}
|
||||
|
||||
// 沿着 presentedViewController 链找到最顶层
|
||||
while let presented = topVC?.presentedViewController {
|
||||
topVC = presented
|
||||
}
|
||||
|
||||
guard let vc = topVC else {
|
||||
NSLog("[MiniPlayer] No view controller found to present share sheet")
|
||||
return
|
||||
}
|
||||
|
||||
NSLog("[MiniPlayer] Presenting from VC: %@", String(describing: type(of: vc)))
|
||||
|
||||
let activityVC = UIActivityViewController(activityItems: [url], applicationActivities: nil)
|
||||
|
||||
// iPad 适配
|
||||
if let popover = activityVC.popoverPresentationController {
|
||||
popover.sourceView = vc.view
|
||||
popover.sourceRect = CGRect(x: vc.view?.bounds.midX ?? 0, y: vc.view?.bounds.midY ?? 0, width: 0, height: 0)
|
||||
popover.permittedArrowDirections = []
|
||||
}
|
||||
|
||||
vc.present(activityVC, animated: true) {
|
||||
NSLog("[MiniPlayer] Share sheet presented successfully")
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Helpers
|
||||
|
||||
private func resolveURL(_ urlString: String, baseURL: URL) -> URL {
|
||||
|
||||
@ -582,12 +582,12 @@ struct BricksAppView: View {
|
||||
}
|
||||
}
|
||||
.sheet(isPresented: Binding<Bool>(
|
||||
get: { bridge.hlsRecorder.pendingExportURL != nil },
|
||||
set: { if !$0 { bridge.hlsRecorder.pendingExportURL = nil } }
|
||||
get: { bridge.pendingExportURL != nil },
|
||||
set: { if !$0 { bridge.pendingExportURL = nil } }
|
||||
)) {
|
||||
if let url = bridge.hlsRecorder.pendingExportURL {
|
||||
if let url = bridge.pendingExportURL {
|
||||
ActivityShareSheet(url: url) {
|
||||
bridge.hlsRecorder.pendingExportURL = nil
|
||||
bridge.pendingExportURL = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -122,7 +122,8 @@ final class PlayerBridge: ObservableObject {
|
||||
#if os(iOS)
|
||||
let hlsRecorder = HLSRecorder()
|
||||
let pipController = PiPController()
|
||||
|
||||
@Published var pendingExportURL: URL?
|
||||
#endif
|
||||
// 诊断日志
|
||||
private var diagLogURL: URL?
|
||||
private var diagLogHandle: FileHandle?
|
||||
@ -144,7 +145,6 @@ final class PlayerBridge: ObservableObject {
|
||||
try? diagLogHandle?.write(contentsOf: data)
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// MARK: - 初始化
|
||||
func setup() {
|
||||
@ -178,6 +178,7 @@ final class PlayerBridge: ObservableObject {
|
||||
#if os(iOS)
|
||||
hlsRecorder.onRecordingSaved = { [weak self] url in
|
||||
self?.showToast("Saved: \(url.lastPathComponent)")
|
||||
self?.pendingExportURL = url
|
||||
}
|
||||
hlsRecorder.onError = { [weak self] msg in
|
||||
self?.showToast(msg)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user