added working view

This commit is contained in:
Jens Timmerman 2020-09-26 15:13:37 +02:00
parent a14d16b91d
commit 99b8814974
5 changed files with 170 additions and 7 deletions

View File

@ -7,6 +7,8 @@
objects = {
/* Begin PBXBuildFile section */
B408F0D7251F6B760043E3A4 /* Tools.swift in Sources */ = {isa = PBXBuildFile; fileRef = B408F0D6251F6B760043E3A4 /* Tools.swift */; };
B408F0DD251F6D180043E3A4 /* AsyncImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = B408F0DC251F6D180043E3A4 /* AsyncImage.swift */; };
B4F0CC7B251BE62B00E9EA74 /* vrtnuApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = B4F0CC7A251BE62B00E9EA74 /* vrtnuApp.swift */; };
B4F0CC7D251BE62B00E9EA74 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B4F0CC7C251BE62B00E9EA74 /* ContentView.swift */; };
B4F0CC7F251BE62F00E9EA74 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B4F0CC7E251BE62F00E9EA74 /* Assets.xcassets */; };
@ -35,6 +37,8 @@
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
B408F0D6251F6B760043E3A4 /* Tools.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tools.swift; sourceTree = "<group>"; };
B408F0DC251F6D180043E3A4 /* AsyncImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AsyncImage.swift; sourceTree = "<group>"; };
B4F0CC77251BE62B00E9EA74 /* vrtnu.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = vrtnu.app; sourceTree = BUILT_PRODUCTS_DIR; };
B4F0CC7A251BE62B00E9EA74 /* vrtnuApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = vrtnuApp.swift; sourceTree = "<group>"; };
B4F0CC7C251BE62B00E9EA74 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
@ -76,6 +80,14 @@
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
B408F0DB251F6D030043E3A4 /* AscyncImage */ = {
isa = PBXGroup;
children = (
B408F0DC251F6D180043E3A4 /* AsyncImage.swift */,
);
path = AscyncImage;
sourceTree = "<group>";
};
B4F0CC6E251BE62B00E9EA74 = {
isa = PBXGroup;
children = (
@ -99,6 +111,7 @@
B4F0CC79251BE62B00E9EA74 /* vrtnu */ = {
isa = PBXGroup;
children = (
B408F0DB251F6D030043E3A4 /* AscyncImage */,
B4F0CC7A251BE62B00E9EA74 /* vrtnuApp.swift */,
B4F0CC7C251BE62B00E9EA74 /* ContentView.swift */,
B4F0CC7E251BE62F00E9EA74 /* Assets.xcassets */,
@ -106,6 +119,7 @@
B4F0CC80251BE62F00E9EA74 /* Preview Content */,
B4F0CCA9251BFD6F00E9EA74 /* video.swift */,
B4F0CCB6251D6B5400E9EA74 /* VrtNuLayout.swift */,
B408F0D6251F6B760043E3A4 /* Tools.swift */,
);
path = vrtnu;
sourceTree = "<group>";
@ -269,6 +283,8 @@
B4F0CCB7251D6B5400E9EA74 /* VrtNuLayout.swift in Sources */,
B4F0CC7B251BE62B00E9EA74 /* vrtnuApp.swift in Sources */,
B4F0CCAA251BFD6F00E9EA74 /* video.swift in Sources */,
B408F0DD251F6D180043E3A4 /* AsyncImage.swift in Sources */,
B408F0D7251F6B760043E3A4 /* Tools.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};

View File

@ -0,0 +1,122 @@
//
// AsyncImage.swift
// AsyncImage
//
// Created by Vadym Bulavin on 2/13/20.
// Copyright © 2020 Vadym Bulavin. All rights reserved.
//
import SwiftUI
import UIKit
import Combine
protocol ImageCache {
subscript(_ url: URL) -> UIImage? { get set }
}
struct TemporaryImageCache: ImageCache {
private let cache = NSCache<NSURL, UIImage>()
subscript(_ key: URL) -> UIImage? {
get { cache.object(forKey: key as NSURL) }
set { newValue == nil ? cache.removeObject(forKey: key as NSURL) : cache.setObject(newValue!, forKey: key as NSURL) }
}
}
struct ImageCacheKey: EnvironmentKey {
static let defaultValue: ImageCache = TemporaryImageCache()
}
extension EnvironmentValues {
var imageCache: ImageCache {
get { self[ImageCacheKey.self] }
set { self[ImageCacheKey.self] = newValue }
}
}
struct AsyncImage<Placeholder: View>: View {
@StateObject private var loader: ImageLoader
private let placeholder: Placeholder
private let image: (UIImage) -> Image
init(
url: URL,
@ViewBuilder placeholder: () -> Placeholder,
@ViewBuilder image: @escaping (UIImage) -> Image = Image.init(uiImage:)
) {
self.placeholder = placeholder()
self.image = image
_loader = StateObject(wrappedValue: ImageLoader(url: url, cache: Environment(\.imageCache).wrappedValue))
}
var body: some View {
content
.onAppear(perform: loader.load)
}
private var content: some View {
Group {
if loader.image != nil {
image(loader.image!)
} else {
placeholder
}
}
}
}
class ImageLoader: ObservableObject {
@Published var image: UIImage?
private(set) var isLoading = false
private let url: URL
private var cache: ImageCache?
private var cancellable: AnyCancellable?
private static let imageProcessingQueue = DispatchQueue(label: "image-processing")
init(url: URL, cache: ImageCache? = nil) {
self.url = url
self.cache = cache
}
deinit {
cancel()
}
func load() {
guard !isLoading else { return }
if let image = cache?[url] {
self.image = image
return
}
cancellable = URLSession.shared.dataTaskPublisher(for: url)
.map { UIImage(data: $0.data) }
.replaceError(with: nil)
.handleEvents(receiveSubscription: { [weak self] _ in self?.onStart() },
receiveOutput: { [weak self] in self?.cache($0) },
receiveCompletion: { [weak self] _ in self?.onFinish() },
receiveCancel: { [weak self] in self?.onFinish() })
.subscribe(on: Self.imageProcessingQueue)
.receive(on: DispatchQueue.main)
.sink { [weak self] in self?.image = $0 }
}
func cancel() {
cancellable?.cancel()
}
private func onStart() {
isLoading = true
}
private func onFinish() {
isLoading = false
}
private func cache(_ image: UIImage?) {
image.map { cache?[url] = $0 }
}
}

View File

@ -8,20 +8,29 @@
import SwiftUI
import AVKit
import AVFoundation
import Combine
struct ContentView: View {
var body: some View {
NavigationView{
List {
VStack {
ForEach(VRTNu().shows, id: \.self){ show in
Section(header: Text(show.title)){
VStack{
Text(show.title)
HStack{
ForEach(show.seasons, id:\.self){ season in
HStack{
ForEach(season.episodes, id:\.self){ episode in
ForEach(season.episodes, id:\.self){ episode in
NavigationLink(destination: VideoView(url: episode.video.hlsUrl)){VStack{
Text(season.seasonName)
Text(episode.name)
}
AsyncImage(
url: episode.imageurl,
placeholder: { Text("Loading ...") },
image: { Image(uiImage: $0).resizable() }
)
}}
}
}
}
@ -34,16 +43,19 @@ struct ContentView: View {
}
}
/*struct SeasonView: View{
var body: some View{
}
URL(string: "https://remix-cf.lwc.vrtcdn.be/remix/ecd69313-4a39-4297-95b1-aede167725b7/remix.ism/.m3u8")!
}*/
struct VideoView: View {
private let player = AVPlayer(url: URL(string: "https://remix-cf.lwc.vrtcdn.be/remix/ecd69313-4a39-4297-95b1-aede167725b7/remix.ism/.m3u8")!);
var url: URL
var body: some View {
let player = AVPlayer(url: url);
VideoPlayer(player: player).fixedSize()
};

9
vrtnu/vrtnu/Tools.swift Normal file
View File

@ -0,0 +1,9 @@
//
// Tools.swift
// vrtnu
//
// Created by Jens Timmerman on 26/09/2020.
//
import Foundation

View File

@ -7,6 +7,7 @@
import Foundation
struct Episode: Hashable{
let name: String
let video: Video
@ -76,6 +77,9 @@ struct VRTNu {
init() {
//TODO: fetch shows
self.shows = [Show(showUrl: URL(string: "https://www.vrt.be/vrtnu/a-z/het-peulengaleis/")!, title: "Het Peulengaleis")]
self.shows = [
Show(showUrl: URL(string: "https://www.vrt.be/vrtnu/a-z/het-peulengaleis/")!, title: "Het Peulengaleis"),
Show(showUrl: URL(string: "https://www.vrt.be/vrtnu/a-z/het-peulengaleis2/")!, title: "Het Peulengaleis 2"),
]
}
}