mirror of
https://github.com/A-Star100/simpliplay-ios.git
synced 2025-09-17 22:39:38 +00:00
add macOS catalyst support
This commit is contained in:
parent
92c240439c
commit
0974bb9ab3
@ -1,8 +1,13 @@
|
|||||||
import SwiftUI
|
import SwiftUI
|
||||||
import AVKit
|
import AVKit
|
||||||
import SafariServices
|
|
||||||
import UniformTypeIdentifiers
|
import UniformTypeIdentifiers
|
||||||
|
|
||||||
|
#if targetEnvironment(macCatalyst)
|
||||||
|
import AppKit
|
||||||
|
#else
|
||||||
|
import SafariServices
|
||||||
|
#endif
|
||||||
|
|
||||||
struct ContentView: View {
|
struct ContentView: View {
|
||||||
@State private var videoURL: String = ""
|
@State private var videoURL: String = ""
|
||||||
@State private var videoFileURL: URL?
|
@State private var videoFileURL: URL?
|
||||||
@ -31,30 +36,23 @@ struct ContentView: View {
|
|||||||
.padding(.horizontal)
|
.padding(.horizontal)
|
||||||
|
|
||||||
HStack {
|
HStack {
|
||||||
Button("Play Media") {
|
Button("Play Media") { playVideo(urlString: videoURL) }
|
||||||
playVideo(urlString: videoURL)
|
.buttonStyle(PrimaryButtonStyle())
|
||||||
}
|
Button("Clear") { videoURL = "" }
|
||||||
.buttonStyle(PrimaryButtonStyle())
|
.foregroundColor(.red)
|
||||||
|
|
||||||
Button("Clear") {
|
|
||||||
videoURL = ""
|
|
||||||
}
|
|
||||||
.foregroundColor(.red)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
VStack(spacing: 12) {
|
VStack(spacing: 12) {
|
||||||
HStack {
|
HStack {
|
||||||
Button("Choose Video File") { isPickingVideo = true }
|
Button("Choose Video File") { pickVideoFile() }
|
||||||
Button("Choose Audio File") { isPickingAudio = true }
|
Button("Choose Audio File") { pickAudioFile() }
|
||||||
}
|
}
|
||||||
|
|
||||||
if let video = videoFileURL {
|
if let video = videoFileURL {
|
||||||
Text("🎞️ \(video.lastPathComponent)")
|
Text("🎞️ \(video.lastPathComponent)")
|
||||||
Button("Play Selected Video") {
|
Button("Play Selected Video") { playLocalVideo(fileURL: video) }
|
||||||
playLocalVideo(fileURL: video)
|
.buttonStyle(PrimaryButtonStyle())
|
||||||
}
|
|
||||||
.buttonStyle(PrimaryButtonStyle())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if let audio = audioFileURL {
|
if let audio = audioFileURL {
|
||||||
@ -63,31 +61,17 @@ struct ContentView: View {
|
|||||||
}
|
}
|
||||||
.padding(.horizontal)
|
.padding(.horizontal)
|
||||||
|
|
||||||
Button("Open Menu") {
|
Button("Open Menu") { isMenuOpen.toggle() }
|
||||||
isMenuOpen.toggle()
|
.buttonStyle(PrimaryButtonStyle())
|
||||||
}
|
|
||||||
.buttonStyle(PrimaryButtonStyle())
|
|
||||||
}
|
}
|
||||||
.padding()
|
.padding()
|
||||||
}
|
}
|
||||||
|
|
||||||
if isMenuOpen {
|
if isMenuOpen {
|
||||||
VStack(alignment: .leading, spacing: 20) {
|
VStack(alignment: .leading, spacing: 20) {
|
||||||
Button("Official Website") {
|
Button("Official Website") { openURL("https://simpliplay.netlify.app") }
|
||||||
urlToOpen = "https://simpliplay.netlify.app"
|
Button("About the Creator") { openURL("https://anirudhsevugan.me") }
|
||||||
showWebView = true
|
Button("Close Menu") { isMenuOpen = false }
|
||||||
isMenuOpen = false
|
|
||||||
}
|
|
||||||
|
|
||||||
Button("About the Creator") {
|
|
||||||
urlToOpen = "https://a-star100.github.io"
|
|
||||||
showWebView = true
|
|
||||||
isMenuOpen = false
|
|
||||||
}
|
|
||||||
|
|
||||||
Button("Close Menu") {
|
|
||||||
isMenuOpen = false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.padding()
|
.padding()
|
||||||
.frame(maxWidth: 250, alignment: .leading)
|
.frame(maxWidth: 250, alignment: .leading)
|
||||||
@ -99,26 +83,10 @@ struct ContentView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.navigationBarHidden(true)
|
.navigationBarHidden(true)
|
||||||
.sheet(isPresented: $showWebView) {
|
|
||||||
if let urlString = urlToOpen, let url = URL(string: urlString) {
|
|
||||||
SafariView(url: url)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.sheet(isPresented: $isPickingVideo) {
|
|
||||||
DocumentPicker(fileType: .movie) { url in
|
|
||||||
videoFileURL = url
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.sheet(isPresented: $isPickingAudio) {
|
|
||||||
DocumentPicker(fileType: .audio) { url in
|
|
||||||
audioFileURL = url
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Playback
|
// MARK: - Playback
|
||||||
|
|
||||||
func playVideo(urlString: String) {
|
func playVideo(urlString: String) {
|
||||||
guard let url = URL(string: urlString) else { return }
|
guard let url = URL(string: urlString) else { return }
|
||||||
let newPlayer = AVPlayer(url: url)
|
let newPlayer = AVPlayer(url: url)
|
||||||
@ -136,23 +104,66 @@ struct ContentView: View {
|
|||||||
|
|
||||||
func presentPlayer() {
|
func presentPlayer() {
|
||||||
guard let player = player else { return }
|
guard let player = player else { return }
|
||||||
|
#if targetEnvironment(macCatalyst)
|
||||||
|
let controller = AVPlayerViewController()
|
||||||
|
controller.player = player
|
||||||
|
controller.view.frame = NSScreen.main?.frame ?? .zero
|
||||||
|
let window = NSWindow(contentViewController: controller)
|
||||||
|
let windowController = NSWindowController(window: window)
|
||||||
|
windowController.showWindow(nil)
|
||||||
|
#else
|
||||||
let controller = AVPlayerViewController()
|
let controller = AVPlayerViewController()
|
||||||
controller.player = player
|
controller.player = player
|
||||||
controller.modalPresentationStyle = .fullScreen
|
controller.modalPresentationStyle = .fullScreen
|
||||||
present(controller)
|
if let rootVC = UIApplication.shared.connectedScenes
|
||||||
|
.compactMap({ ($0 as? UIWindowScene)?.windows.first?.rootViewController }).first {
|
||||||
|
rootVC.present(controller, animated: true)
|
||||||
|
}
|
||||||
|
#endif
|
||||||
player.play()
|
player.play()
|
||||||
}
|
}
|
||||||
|
|
||||||
func present(_ viewController: UIViewController) {
|
// MARK: - File Picking
|
||||||
if let rootVC = UIApplication.shared.connectedScenes
|
func pickVideoFile() {
|
||||||
.compactMap({ ($0 as? UIWindowScene)?.windows.first?.rootViewController }).first {
|
#if targetEnvironment(macCatalyst)
|
||||||
rootVC.present(viewController, animated: true)
|
let panel = NSOpenPanel()
|
||||||
|
panel.allowedContentTypes = [.movie]
|
||||||
|
panel.allowsMultipleSelection = false
|
||||||
|
if panel.runModal() == .OK {
|
||||||
|
videoFileURL = panel.url
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
isPickingVideo = true
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
func pickAudioFile() {
|
||||||
|
#if targetEnvironment(macCatalyst)
|
||||||
|
let panel = NSOpenPanel()
|
||||||
|
panel.allowedContentTypes = [.audio]
|
||||||
|
panel.allowsMultipleSelection = false
|
||||||
|
if panel.runModal() == .OK {
|
||||||
|
audioFileURL = panel.url
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
isPickingAudio = true
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Open URL
|
||||||
|
func openURL(_ urlString: String) {
|
||||||
|
#if targetEnvironment(macCatalyst)
|
||||||
|
if let url = URL(string: urlString) {
|
||||||
|
NSWorkspace.shared.open(url)
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
urlToOpen = urlString
|
||||||
|
showWebView = true
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Observer to Keep Screen On
|
// MARK: - Observer
|
||||||
|
|
||||||
class PlayerObserver: NSObject, ObservableObject {
|
class PlayerObserver: NSObject, ObservableObject {
|
||||||
private var observation: NSKeyValueObservation?
|
private var observation: NSKeyValueObservation?
|
||||||
private var player: AVPlayer?
|
private var player: AVPlayer?
|
||||||
@ -163,23 +174,28 @@ class PlayerObserver: NSObject, ObservableObject {
|
|||||||
|
|
||||||
observation = newPlayer.observe(\.timeControlStatus, options: [.initial, .new]) { player, _ in
|
observation = newPlayer.observe(\.timeControlStatus, options: [.initial, .new]) { player, _ in
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
|
#if !targetEnvironment(macCatalyst)
|
||||||
UIApplication.shared.isIdleTimerDisabled = player.timeControlStatus == .playing
|
UIApplication.shared.isIdleTimerDisabled = player.timeControlStatus == .playing
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
NotificationCenter.default.addObserver(forName: .AVPlayerItemDidPlayToEndTime, object: newPlayer.currentItem, queue: .main) { _ in
|
NotificationCenter.default.addObserver(forName: .AVPlayerItemDidPlayToEndTime, object: newPlayer.currentItem, queue: .main) { _ in
|
||||||
|
#if !targetEnvironment(macCatalyst)
|
||||||
UIApplication.shared.isIdleTimerDisabled = false
|
UIApplication.shared.isIdleTimerDisabled = false
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
deinit {
|
deinit {
|
||||||
observation?.invalidate()
|
observation?.invalidate()
|
||||||
|
#if !targetEnvironment(macCatalyst)
|
||||||
UIApplication.shared.isIdleTimerDisabled = false
|
UIApplication.shared.isIdleTimerDisabled = false
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Styles and Views
|
// MARK: - Button Style
|
||||||
|
|
||||||
struct PrimaryButtonStyle: ButtonStyle {
|
struct PrimaryButtonStyle: ButtonStyle {
|
||||||
func makeBody(configuration: Configuration) -> some View {
|
func makeBody(configuration: Configuration) -> some View {
|
||||||
configuration.label
|
configuration.label
|
||||||
@ -192,48 +208,3 @@ struct PrimaryButtonStyle: ButtonStyle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct SafariView: View {
|
|
||||||
var url: URL
|
|
||||||
var body: some View {
|
|
||||||
SafariViewController(url: url)
|
|
||||||
.edgesIgnoringSafeArea(.all)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct SafariViewController: UIViewControllerRepresentable {
|
|
||||||
let url: URL
|
|
||||||
func makeUIViewController(context: Context) -> SFSafariViewController {
|
|
||||||
SFSafariViewController(url: url)
|
|
||||||
}
|
|
||||||
func updateUIViewController(_ uiViewController: SFSafariViewController, context: Context) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct DocumentPicker: UIViewControllerRepresentable {
|
|
||||||
var fileType: UTType
|
|
||||||
var onPick: (URL) -> Void
|
|
||||||
|
|
||||||
func makeCoordinator() -> Coordinator {
|
|
||||||
Coordinator(onPick: onPick)
|
|
||||||
}
|
|
||||||
|
|
||||||
func makeUIViewController(context: Context) -> UIDocumentPickerViewController {
|
|
||||||
let picker = UIDocumentPickerViewController(forOpeningContentTypes: [fileType], asCopy: true)
|
|
||||||
picker.delegate = context.coordinator
|
|
||||||
return picker
|
|
||||||
}
|
|
||||||
|
|
||||||
func updateUIViewController(_ uiViewController: UIDocumentPickerViewController, context: Context) {}
|
|
||||||
|
|
||||||
class Coordinator: NSObject, UIDocumentPickerDelegate {
|
|
||||||
let onPick: (URL) -> Void
|
|
||||||
init(onPick: @escaping (URL) -> Void) {
|
|
||||||
self.onPick = onPick
|
|
||||||
}
|
|
||||||
|
|
||||||
func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
|
|
||||||
if let url = urls.first {
|
|
||||||
onPick(url)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
Loading…
Reference in New Issue
Block a user