From 947969a248dccdba133539be2ca7f5e309763b0a Mon Sep 17 00:00:00 2001 From: DJYohann Date: Sun, 21 May 2023 23:01:03 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=92=84=20-=20finish=20views?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../PodcastsApp.xcodeproj/project.pbxproj | 12 ++++ src/PodcastsApp/PodcastsApp/ContentView.swift | 2 +- src/PodcastsApp/PodcastsApp/Model/Stub.swift | 27 ++++++-- .../PodcastsApp/Utils/Extensions.swift | 25 +++++++ .../PodcastsApp/Views/NowPlayingBar.swift | 69 +++++++++++-------- .../PodcastsApp/Views/PodcastCover.swift | 61 ++++++++-------- .../PodcastsApp/Views/PodcastEpisode.swift | 48 ++++--------- .../PodcastsApp/Views/PodcastListItem.swift | 28 +++----- .../PodcastsApp/Views/PodcastView.swift | 13 ++-- 9 files changed, 155 insertions(+), 130 deletions(-) create mode 100644 src/PodcastsApp/PodcastsApp/Utils/Extensions.swift diff --git a/src/PodcastsApp/PodcastsApp.xcodeproj/project.pbxproj b/src/PodcastsApp/PodcastsApp.xcodeproj/project.pbxproj index 50d87b4..1eb3fef 100644 --- a/src/PodcastsApp/PodcastsApp.xcodeproj/project.pbxproj +++ b/src/PodcastsApp/PodcastsApp.xcodeproj/project.pbxproj @@ -18,6 +18,7 @@ 1E92BCEF2A14319D0026C641 /* PodcastListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E92BCEE2A14319D0026C641 /* PodcastListView.swift */; }; 1E92BCF12A1439000026C641 /* PodcastView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E92BCF02A1439000026C641 /* PodcastView.swift */; }; 1E92BCF32A1440B20026C641 /* LibraryMenuItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E92BCF22A1440B20026C641 /* LibraryMenuItem.swift */; }; + 1EE5D3E72A196A7C00F92680 /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1EE5D3E62A196A7C00F92680 /* Extensions.swift */; }; 1EFF14972A10E76A0018278E /* PodcastsAppApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1EFF14962A10E76A0018278E /* PodcastsAppApp.swift */; }; 1EFF14992A10E76A0018278E /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1EFF14982A10E76A0018278E /* ContentView.swift */; }; 1EFF149B2A10E76C0018278E /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 1EFF149A2A10E76C0018278E /* Assets.xcassets */; }; @@ -59,6 +60,7 @@ 1E92BCEE2A14319D0026C641 /* PodcastListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PodcastListView.swift; sourceTree = ""; }; 1E92BCF02A1439000026C641 /* PodcastView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PodcastView.swift; sourceTree = ""; }; 1E92BCF22A1440B20026C641 /* LibraryMenuItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LibraryMenuItem.swift; sourceTree = ""; }; + 1EE5D3E62A196A7C00F92680 /* Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Extensions.swift; sourceTree = ""; }; 1EFF14932A10E76A0018278E /* PodcastsApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = PodcastsApp.app; sourceTree = BUILT_PRODUCTS_DIR; }; 1EFF14962A10E76A0018278E /* PodcastsAppApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PodcastsAppApp.swift; sourceTree = ""; }; 1EFF14982A10E76A0018278E /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; @@ -99,6 +101,14 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 1EE5D3E82A196A8200F92680 /* Utils */ = { + isa = PBXGroup; + children = ( + 1EE5D3E62A196A7C00F92680 /* Extensions.swift */, + ); + path = Utils; + sourceTree = ""; + }; 1EFF148A2A10E76A0018278E = { isa = PBXGroup; children = ( @@ -122,6 +132,7 @@ 1EFF14952A10E76A0018278E /* PodcastsApp */ = { isa = PBXGroup; children = ( + 1EE5D3E82A196A8200F92680 /* Utils */, 1EFF14C52A1116430018278E /* Model */, 1EFF14C02A10F3AC0018278E /* Views */, 1EFF14962A10E76A0018278E /* PodcastsAppApp.swift */, @@ -327,6 +338,7 @@ 1E8272F12A1597C8005837D3 /* MenuItem.swift in Sources */, 1E8272EF2A1584CC005837D3 /* LibraryMenu.swift in Sources */, 1E8272EB2A151FA6005837D3 /* Stub.swift in Sources */, + 1EE5D3E72A196A7C00F92680 /* Extensions.swift in Sources */, 1E92BCF12A1439000026C641 /* PodcastView.swift in Sources */, 1EFF14C72A1116D90018278E /* PodcastEpisode.swift in Sources */, 1E92BCEB2A14032D0026C641 /* Podcast.swift in Sources */, diff --git a/src/PodcastsApp/PodcastsApp/ContentView.swift b/src/PodcastsApp/PodcastsApp/ContentView.swift index 0b6ebd5..b19e81a 100644 --- a/src/PodcastsApp/PodcastsApp/ContentView.swift +++ b/src/PodcastsApp/PodcastsApp/ContentView.swift @@ -52,7 +52,7 @@ struct ContentView: View { } .overlay(VStack { Spacer() - NowPlayingBar() + NowPlayingBar(podcast: podcastList[0]) .frame(height: 162) }) } diff --git a/src/PodcastsApp/PodcastsApp/Model/Stub.swift b/src/PodcastsApp/PodcastsApp/Model/Stub.swift index 7fb5ec9..7273a9e 100644 --- a/src/PodcastsApp/PodcastsApp/Model/Stub.swift +++ b/src/PodcastsApp/PodcastsApp/Model/Stub.swift @@ -30,11 +30,21 @@ let podcastList : [Podcast] = [ topic: "Technologies", lastUpdateDate: Date.now, episodes: [ - Episode(title: "Salut", description: "Ceci est un texte", duration: 34), - Episode(title: "Salut", description: "Ceci est un texte", duration: 34), - Episode(title: "Salut", description: "Ceci est un texte", duration: 34), - Episode(title: "Salut", description: "Ceci est un texte", duration: 34), - Episode(title: "Salut", description: "Ceci est un texte", duration: 34), + Episode(title: "L'histoire de la triche sur CS:GO", + description: "Le jeu CS:GO est connu pour être gangrené par la triche, y compris dans les sphères professionnels et la scène esport. Pourquoi CS:GO est particulièrement touché ? D’où ça vient techniquement, et peut-on y faire quelque-chose ? On remonte l’histoire, qui prend racine en 2014, à quelques semaines de la DreamHack Winter, le plus gros évènement CS:GO de l’année !", + duration: 32), + Episode(title: "L'assistant vocal d'Underscore", + description: "Nos assistants vocaux ne sont pas très efficaces… mis à part peut-être pour lancer un minuteur. Mais est-il possible d’en créer un de toute pièce, correspondant à nos envies et à ce qu’on aime ? Avec les nouveaux services d’IA sortis ces derniers mois, on a fait le test, et on vous fait une petite démo en direct ! “OK Michel, écoute Underscore_”", + duration: 30, + isPlayed: true), + Episode(title: "Le témoignage de cet étudiant arnaqué par le directeur de son école", + description: "Un ancien étudiant vient témoigner de son expérience à SUPINFO. Derrière des pratiques parfois douteuses se cachaient en réalité une organisation quasi mafieuse et un patron prêt à tout pour détourner de l’argent ! Aujourd’hui, il semble pourtant avoir récidivé…", + duration: 47, + isPlayed: true, + isDownloaded: true), + Episode(title: "La révolution du stockage est en marche", + description: "Une nouvelle technologie de mémoire, la ReRAM, pourrait bien bouleverser le secteur et faire radicalement chuter son prix dans les années à venir. Édouard, qui a étudié le sujet pendant trois ans, revient pour nous sur cette technologie très prometteuse !", + duration: 34), ]), Podcast( name: "Popcorn", @@ -43,7 +53,12 @@ let podcastList : [Podcast] = [ nbStars: 4.9, nbEvaluations: 709, topic: "Actualité du divertissement", - lastUpdateDate: Date.now), + lastUpdateDate: Date.now, + episodes: [ + Episode(title: "Le futur des écrans se déroule devant vous", + description: "PP Garcia nous présente Mardi Turfu, la rubrique tech de Popcorn ! Et pour cette semaine il nous parle des futurs des écrans, mais pas que !", + duration: 21) + ]), Podcast( name: "Un bon moment avec Kyan KHOJANDI et NAVO", creator: "Kyan Khojandi & navo", diff --git a/src/PodcastsApp/PodcastsApp/Utils/Extensions.swift b/src/PodcastsApp/PodcastsApp/Utils/Extensions.swift new file mode 100644 index 0000000..61f35b2 --- /dev/null +++ b/src/PodcastsApp/PodcastsApp/Utils/Extensions.swift @@ -0,0 +1,25 @@ +// +// Extensions.swift +// PodcastsApp +// +// Created by BREUIL Yohann on 20/05/2023. +// + +import Foundation +import UIKit + +extension UIImage { + var averageColor: UIColor? { + guard let inputImage = CIImage(image: self) else { return nil } + let extentVector = CIVector(x: inputImage.extent.origin.x, y: inputImage.extent.origin.y, z: inputImage.extent.size.width, w: inputImage.extent.size.height) + + guard let filter = CIFilter(name: "CIAreaAverage", parameters: [kCIInputImageKey: inputImage, kCIInputExtentKey: extentVector]) else { return nil } + guard let outputImage = filter.outputImage else { return nil } + + var bitmap = [UInt8](repeating: 0, count: 4) + let context = CIContext(options: [.workingColorSpace: kCFNull]) + context.render(outputImage, toBitmap: &bitmap, rowBytes: 4, bounds: CGRect(x: 0, y: 0, width: 1, height: 1), format: .RGBA8, colorSpace: nil) + + return UIColor(red: CGFloat(bitmap[0]) / 255, green: CGFloat(bitmap[1]) / 255, blue: CGFloat(bitmap[2]) / 255, alpha: CGFloat(bitmap[3]) / 255) + } +} diff --git a/src/PodcastsApp/PodcastsApp/Views/NowPlayingBar.swift b/src/PodcastsApp/PodcastsApp/Views/NowPlayingBar.swift index 776dc23..c6a7581 100644 --- a/src/PodcastsApp/PodcastsApp/Views/NowPlayingBar.swift +++ b/src/PodcastsApp/PodcastsApp/Views/NowPlayingBar.swift @@ -8,44 +8,57 @@ import SwiftUI struct NowPlayingBar : View { + var podcast : Podcast + var body: some View { - ZStack { - Rectangle() - .foregroundColor(Color.black.opacity(0.2)) - .frame(width: UIScreen.main.bounds.size.width, height: 65) - HStack { - Button(action: {}) { - HStack { - Image("Cover") - .resizable() - .frame(width: 45, height: 45) - .shadow(radius: 6, x: 0, y: 3) - .padding(.leading) - Text("Shake It Off").padding(.leading, 10) - Spacer() + HStack { + Button(action: {}) { + HStack { + Image(podcast.coverImage) + .resizable() + .cornerRadius(8) + .frame(width: 50, height: 50) + .shadow(radius: 6, x: 0, y: 3) + + + VStack(alignment: .leading) { + Text(podcast.episodes[0].title) + .frame(width: 150) + .truncationMode(.tail) + .font(.body) + .bold() + + Text("29 Avril 2023") + .font(.caption) } - } - .buttonStyle(PlainButtonStyle()) - - Button(action: {}) { - Image(systemName: "play.fill").font(.title3) - } - .buttonStyle(PlainButtonStyle()).padding(.horizontal) - Button(action: {}) { - Image(systemName: "forward.fill").font(.title3) + Spacer() } - .buttonStyle(PlainButtonStyle()) - .padding(.trailing, 30) + .padding() } + .buttonStyle(.plain) + + Button(action: {}) { + Image(systemName: "play.fill").font(.title3) + } + .buttonStyle(.plain) + .padding(.horizontal) + + Button(action: {}) { + Image(systemName: "goforward.30").font(.title3) + } + .buttonStyle(.plain) + .padding(.trailing, 30) } - + .frame(width: UIScreen.main.bounds.size.width, height: 65) } - } struct NowPlayingBar_Previews: PreviewProvider { static var previews: some View { - NowPlayingBar() + Group { + NowPlayingBar(podcast: podcastList[0]) + NowPlayingBar(podcast: podcastList[0]).preferredColorScheme(.dark) + } } } diff --git a/src/PodcastsApp/PodcastsApp/Views/PodcastCover.swift b/src/PodcastsApp/PodcastsApp/Views/PodcastCover.swift index 8fab270..bf093a0 100644 --- a/src/PodcastsApp/PodcastsApp/Views/PodcastCover.swift +++ b/src/PodcastsApp/PodcastsApp/Views/PodcastCover.swift @@ -11,42 +11,37 @@ struct PodcastCover: View { var podcast: Podcast var body: some View { - ZStack { - Rectangle() - .fill(.cyan) - - VStack { - Image(podcast.coverImage) - .resizable() - .aspectRatio(contentMode: .fit) - .frame(width: 300, height: 300) - Text(podcast.name) - Text(podcast.creator) - - Button(action: {}) { - Image(systemName: "play.fill") - Text("Reprendre") - } + VStack { + Image(podcast.coverImage) + .resizable() + .aspectRatio(contentMode: .fit) + .frame(width: 200, height: 200) + .cornerRadius(8) + .opacity(20) + .shadow(radius: 20) + Text(podcast.name) + .font(.title2) .bold() - .frame(width: 200, height: 20, alignment: .center) - .cornerRadius(20) - .padding() - .background( - RoundedRectangle( - cornerRadius: 20, - style: .continuous - ) - .fill(.yellow)) - .overlay { - RoundedRectangle( - cornerRadius: 20, - style: .continuous - ) - .stroke(.pink, lineWidth: 2) - } - } + Text(podcast.creator) + .font(.title3) + Button(action: {}) { + Image(systemName: "play.fill") + Text("Reprendre") + .font(.headline) + } + .bold() + .frame(width: 250, height: 25, alignment: .center) + .padding() + .background( + RoundedRectangle( + cornerRadius: 8, + style: .continuous + ) + .fill(.white)) } + .frame(width: UIScreen.main.bounds.width, height: 400) + .background(Color(uiColor: UIImage(imageLiteralResourceName: podcast.coverImage).averageColor ?? .white)) } } diff --git a/src/PodcastsApp/PodcastsApp/Views/PodcastEpisode.swift b/src/PodcastsApp/PodcastsApp/Views/PodcastEpisode.swift index 3ff2027..da9f455 100644 --- a/src/PodcastsApp/PodcastsApp/Views/PodcastEpisode.swift +++ b/src/PodcastsApp/PodcastsApp/Views/PodcastEpisode.swift @@ -12,7 +12,8 @@ struct PodcastEpisode: View { var body: some View { VStack(alignment: .leading) { - Text("5 Mai") + // Date + Text(episode.datePublication.formatted(date: .abbreviated, time: .omitted)) .font(.caption) .bold() .textCase(.uppercase) @@ -20,11 +21,11 @@ struct PodcastEpisode: View { // Title if episode.isPlayed { Text(episode.title) - .font(.title2) + .font(.headline) .bold() } else { Text(episode.title) - .font(.title3) + .font(.headline) } // Description @@ -36,7 +37,7 @@ struct PodcastEpisode: View { Button(action: {}) { Image(systemName: "play.fill") } - .frame(width: 33, height: 33) + .buttonStyle(PlainButtonStyle()) .foregroundColor(Color.purple) .background(Color("#747476")) .clipShape(Circle()) @@ -48,12 +49,13 @@ struct PodcastEpisode: View { .foregroundColor(Color.accentColor) .bold() } else { - Text("\(episode.duration)") + Text("\(episode.duration) min") .foregroundColor(Color.accentColor) .bold() } Spacer() + if (episode.isDownloaded) { Image(systemName: "arrow.down.circle.fill") } @@ -62,43 +64,21 @@ struct PodcastEpisode: View { } } .padding() - .frame(width: UIScreen.main.bounds.width, height: 200) + .frame(width: UIScreen.main.bounds.width, height: 160) } } struct PodcastEpisodeDetail_Previews: PreviewProvider { static var previews: some View { - let episode1: Episode = Episode( - title: "L'assistant vocal d'Underscore", - description: "Texte qui ne décrit pas grand chose mais c'est intéressant de marquer beaucoup de texte juste pour écrire. C'est long mais bon c'est la vie, la vie c'est l'inverse de la mort. Je peux encore écrire pendant longtemts mais là j'ai une flemme.", - datePublication: Date.now, - duration: 30, - isPlayed: true) - - let episode2: Episode = Episode( - title: "L'assistant vocal d'Underscore", - description: "Texte qui ne décrit pas grand chose mais c'est intéressant de marquer beaucoup de texte juste pour écrire. C'est long mais bon c'est la vie, la vie c'est l'inverse de la mort. Je peux encore écrire pendant longtemts mais là j'ai une flemme.", - datePublication: Date.now, - duration: 30, - isPlayed: false) - - let episode3: Episode = Episode( - title: "L'assistant vocal d'Underscore", - description: "Texte qui ne décrit pas grand chose mais c'est intéressant de marquer beaucoup de texte juste pour écrire. C'est long mais bon c'est la vie, la vie c'est l'inverse de la mort. Je peux encore écrire pendant longtemts mais là j'ai une flemme.", - datePublication: Date.now, - duration: 30, - isPlayed: true, - isDownloaded: true) - Group { - PodcastEpisode(episode: episode1) - PodcastEpisode(episode: episode1).preferredColorScheme(.dark) + PodcastEpisode(episode: podcastList[0].episodes[0]) + PodcastEpisode(episode: podcastList[0].episodes[0]).preferredColorScheme(.dark) - PodcastEpisode(episode: episode2) - PodcastEpisode(episode: episode2).preferredColorScheme(.dark) + PodcastEpisode(episode: podcastList[0].episodes[1]) + PodcastEpisode(episode: podcastList[0].episodes[1]).preferredColorScheme(.dark) - PodcastEpisode(episode: episode3) - PodcastEpisode(episode: episode3).preferredColorScheme(.dark) + PodcastEpisode(episode: podcastList[0].episodes[2]) + PodcastEpisode(episode: podcastList[0].episodes[2]).preferredColorScheme(.dark) } } diff --git a/src/PodcastsApp/PodcastsApp/Views/PodcastListItem.swift b/src/PodcastsApp/PodcastsApp/Views/PodcastListItem.swift index c7f97ca..d8de9a0 100644 --- a/src/PodcastsApp/PodcastsApp/Views/PodcastListItem.swift +++ b/src/PodcastsApp/PodcastsApp/Views/PodcastListItem.swift @@ -14,33 +14,21 @@ struct PodcastListItem: View { VStack(alignment: .leading) { Image(podcast.coverImage) .resizable() - .aspectRatio(contentMode: .fill) - .cornerRadius(8) - VStack(alignment: .leading) { - Text(podcast.name) - .bold() - Text("Mise à jour : Il y a 2j") - } - .aspectRatio(contentMode: .fit) + .scaledToFill() + .cornerRadius(6) + Text(podcast.name) + .bold() + Text("Mise à jour : Il y a 2j") } - .frame(width: 175, height: 175) + .frame(width: 150, height: 150) } } struct PodcastListDetail_Previews: PreviewProvider { static var previews: some View { - let podcast: Podcast = Podcast( - name: "Underscore_", - creator: "Micode", - coverImage: "underscore_podcast", - nbStars: 4.7, - nbEvaluations: 963, - topic: "Technologies", - lastUpdateDate: Date.now) - Group { - PodcastListItem(podcast: podcast) - PodcastListItem(podcast: podcast).preferredColorScheme(.dark) + PodcastListItem(podcast: podcastList[0]) + PodcastListItem(podcast: podcastList[0]).preferredColorScheme(.dark) } } } diff --git a/src/PodcastsApp/PodcastsApp/Views/PodcastView.swift b/src/PodcastsApp/PodcastsApp/Views/PodcastView.swift index b247a33..59186ca 100644 --- a/src/PodcastsApp/PodcastsApp/Views/PodcastView.swift +++ b/src/PodcastsApp/PodcastsApp/Views/PodcastView.swift @@ -18,30 +18,27 @@ struct PodcastView: View { VStack { PodcastCover(podcast: podcast) - LazyVGrid(columns: columns, spacing: 10, pinnedViews: [.sectionHeaders]) { - Section { - HStack { - Text("Épisodes") - } - } - + LazyVStack { ForEach (podcast.episodes) { episode in PodcastEpisode(episode: episode) + Divider() } } } } .toolbar { - HStack { + ToolbarItemGroup { Button(action: {}) { Image(systemName: "checkmark") } + .frame(width: 30, height: 30) .clipShape(Circle()) .buttonStyle(.bordered) Button(action: {}) { Image(systemName: "ellipsis") } + .frame(width: 30, height: 30) .clipShape(Circle()) .buttonStyle(.bordered) }