Skip to content

Instantly share code, notes, and snippets.

@mazz
Created November 25, 2023 18:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mazz/f256ae5cdf373b7f5e73d5ccc914b2a2 to your computer and use it in GitHub Desktop.
Save mazz/f256ae5cdf373b7f5e73d5ccc914b2a2 to your computer and use it in GitHub Desktop.
import SwiftUI
import LNPopupUI
import LNPopupController
import Combine
import KSPlayer
struct BlurView: UIViewRepresentable {
var style: UIBlurEffect.Style = .systemMaterial
func makeUIView(context: Context) -> UIVisualEffectView {
return UIVisualEffectView(effect: UIBlurEffect(style: style))
}
func updateUIView(_ uiView: UIVisualEffectView, context: Context) {
uiView.effect = UIBlurEffect(style: style)
}
}
struct PopupPlayerView: View {
@EnvironmentObject private var appModel: AppModel
let imageDownloader = ImageDownloader()
@State private var thumbImage: UIImage?
let song: LivestreamModel
@State var playbackProgress: Float = Float.random(in: 0..<1)
@State var volume: Float = Float.random(in: 0..<1)
@State var isPlaying: Bool = true
init(song: LivestreamModel) {
self.song = song
}
var body: some View {
GeometryReader { geometry in
let safeArea = geometry.safeAreaInsets
VStack {
KSVideoPlayerView(model: song)
VStack(spacing: geometry.size.height * 30.0 / 896.0) {
ScrollView(.vertical, showsIndicators: false) {
VStack(spacing: 10) {
ForEach(1...5, id: \.self) { index in
GeometryReader {
let size = $0.size
RoundedRectangle(cornerRadius: 15, style: .continuous)
.fill(.red.gradient)
.frame(width: size.width, height: size.height)
}
.frame(height: 220)
}
}
}
}
.padding(geometry.size.height * 40.0 / 896.0)
}
.frame(minWidth: 0,
maxWidth: .infinity,
minHeight: 0,
maxHeight: .infinity,
alignment: .top)
.background({
ZStack {
Image(systemName: "music.mic.circle.fill")
.resizable()
BlurView()
}
.edgesIgnoringSafeArea(.all)
}())
}
.popupTitle(song.title, subtitle: song.sourceMaterial)
.popupImage(
Image(uiImage: thumbImage ?? UIImage(named: "faithful-word-logo-gray-720.png")!)
.resizable()
)
.popupProgress(playbackProgress)
.popupBarItems({
HStack(spacing: 20) {
Button(action: {
isPlaying.toggle()
}) {
Image(systemName: isPlaying ? "pause.fill" : "play.fill")
.foregroundColor(Color("Theme"))
}
Button(action: {
appModel.tappedPopupPlayerClose()
print("Close")
}) {
Image(systemName: "xmark")
.foregroundColor(Color("Theme"))
}
}
})
.onAppear {
Task {
do {
thumbImage = try await imageDownloader.downloadImage(from: URL(string: song.posterUrl)!)
// Display the image
// thumbImage = image
} catch {
print("Error: \(error.localizedDescription)")
}
}
}
}
}
class ImageCache {
private var cache: [URL: UIImage] = [:]
func cacheImage(_ image: UIImage, for url: URL) {
cache[url] = image
}
func image(for url: URL) -> UIImage? {
return cache[url]
}
}
class ImageDownloader {
private let imageCache = ImageCache()
func downloadImage(from url: URL) async throws -> UIImage {
if let cachedImage = imageCache.image(for: url) {
return cachedImage
}
let data = try await downloadImageData(from: url)
if let image = UIImage(data: data) {
// throw NSError(domain: "InvalidImageData", code: 0, userInfo: nil)
imageCache.cacheImage(image, for: url)
return image
} else {
if let image = UIImage(named: "faithful-word-logo-gray-720.png") {
imageCache.cacheImage(image, for: url)
return image
} else {
throw NSError(domain: "InvalidImageData", code: 0, userInfo: nil)
}
}
}
func downloadImageData(from url: URL) async throws -> Data {
let request = URLRequest(url: url)
let (data, _) = try await URLSession.shared.data(for: request)
return data
}
}
@mazz
Copy link
Author

mazz commented Nov 25, 2023

it is called from ContentView.

//
//  ContentView.swift
//  WebSocketHarness
//
//  Created by Tycho on 2023-10-21.
//

import SwiftUI
import KSPlayer

struct ContentView: View {
    @EnvironmentObject
    private var appModel: AppModel

//    @State var showLivestreams: Bool = false
//    @State var showRecentPlayList: Bool = false

    @State var isPopupBarPresented: Bool = false
    @State var isPopupOpen: Bool = false
    @State var tappedLivestream: LivestreamModel?

    private var initialView: some View {
        #if os(macOS)
        NavigationSplitView {
            List(selection: $appModel.tabSelected) {
                link(to: .Home)
                link(to: .Favorite)
                link(to: .Files)
            }
        } detail: {
            appModel.tabSelected.destination(appModel: appModel)
        }
        #else
        TabView(selection: $appModel.tabSelected) {
            tab(to: .Home)
            tab(to: .Favorite)
            tab(to: .Setting)
        }
        .onReceive(appModel.tappedLivestream, perform: { model in
            tappedLivestream = model
            DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
                isPopupBarPresented = true
                isPopupOpen = true
            }
        })
        .onReceive(appModel.shouldClosePopupPlayer, perform: { close in
            isPopupOpen = !close
            isPopupBarPresented = !close
        })
        .onReceive(appModel.shouldStopPlayback, perform: { stop in
//            isPopupOpen = !close
//            isPopupBarPresented = !close
        })
        .popup(isBarPresented: $isPopupBarPresented, isPopupOpen: $isPopupOpen) {
            if let tappedLivestream {
                PopupPlayerView(song: tappedLivestream)
            }
        }

        #endif
    }

    var body: some View {
        initialView
            .preferredColorScheme(.dark)
            .background(Color.black)

    }
    
    func tab(to item: TabBarItem) -> some View {
        Group {
            if item == .Home {
                NavigationStack(path: $appModel.path) {
                    item.destination(appModel: appModel)
                }
            } else {
                NavigationStack {
                    item.destination(appModel: appModel)
                }
            }
        }
        .tabItem {
            item.lable.tag(item)
        }.tag(item)
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

enum TabBarItem: Int {
    case Home
    case Favorite
    case Setting
    var lable: Label<Text, Image> {
        switch self {
        case .Home:
            return Label("Home", systemImage: "house.fill")
        case .Favorite:
            return Label("Favorite", systemImage: "star.fill")

        case .Setting:
            return Label("Setting", systemImage: "gear")
        }
    }

    @ViewBuilder
    func destination(appModel: AppModel) -> some View {
        switch self {
        case .Home:
            HomeView()
                .navigationPlay()
        case .Favorite:
            FavoriteView()
                .navigationPlay()
        case .Setting:
            SettingView()
        }
    }
}

public extension View {
    @ViewBuilder
    func navigationPlay() -> some View {
        navigationDestination(for: URL.self) { url in
            KSVideoPlayerView(url: url, options: KSOptions())
            #if !os(macOS)
                .toolbar(.hidden, for: .tabBar)
            #endif
        }
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment