- Skip BricksView, render video directly in SwiftUI (fixes 1/3 height) - Fullscreen uses plain NSView+AVPlayerLayer (fixes objc_release crash) - Remove NSApp.hide(nil) (fixes fullscreen not showing) - Add volume +/- buttons and volume slider indicator - Add iOS/iPadOS support with #if os guards - ProgressSlider decoupled from BricksEngine - PlayerBridge no longer depends on player.ui JSON
47 lines
1.6 KiB
Swift
47 lines
1.6 KiB
Swift
import SwiftUI
|
||
import AVFoundation
|
||
|
||
/// ProgressSlider — 纯SwiftUI进度条,拖动seek
|
||
struct ProgressSlider: View {
|
||
let player: AVPlayer
|
||
@ObservedObject var bridge: PlayerBridge
|
||
@State private var isDragging = false
|
||
|
||
var body: some View {
|
||
GeometryReader { geo in
|
||
ZStack(alignment: .leading) {
|
||
RoundedRectangle(cornerRadius: 2)
|
||
.fill(Color.secondary.opacity(0.3))
|
||
.frame(height: 4)
|
||
|
||
RoundedRectangle(cornerRadius: 2)
|
||
.fill(Color.accentColor)
|
||
.frame(width: geo.size.width * bridge.progressRatio, height: 4)
|
||
|
||
Circle()
|
||
.fill(Color.accentColor)
|
||
.frame(width: 14, height: 14)
|
||
.offset(x: geo.size.width * bridge.progressRatio - 7)
|
||
.shadow(radius: 2)
|
||
}
|
||
.frame(height: 20)
|
||
.contentShape(Rectangle())
|
||
.gesture(
|
||
DragGesture(minimumDistance: 0)
|
||
.onChanged { value in
|
||
isDragging = true
|
||
let ratio = max(0, min(1, value.location.x / geo.size.width))
|
||
if bridge.cachedDuration > 0 {
|
||
let seekTime = ratio * bridge.cachedDuration
|
||
player.seek(to: CMTime(seconds: seekTime, preferredTimescale: 600))
|
||
}
|
||
}
|
||
.onEnded { _ in
|
||
isDragging = false
|
||
}
|
||
)
|
||
}
|
||
.frame(height: 20)
|
||
}
|
||
}
|