diff --git a/Halmap.xcodeproj/project.pbxproj b/Halmap.xcodeproj/project.pbxproj index 08f9a01..2b31e5b 100644 --- a/Halmap.xcodeproj/project.pbxproj +++ b/Halmap.xcodeproj/project.pbxproj @@ -15,6 +15,7 @@ 87F6ECF1291677AC004533C4 /* Halmap+UIApplication.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87F6ECF0291677AC004533C4 /* Halmap+UIApplication.swift */; }; 87F6ECF52916B331004533C4 /* Halmap+UINavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87F6ECF42916B331004533C4 /* Halmap+UINavigationController.swift */; }; 87F6ECF72916BB44004533C4 /* Halmap+UIView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87F6ECF62916BB44004533C4 /* Halmap+UIView.swift */; }; + A813C77429C779C400CF73FE /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = A813C77329C779C400CF73FE /* GoogleService-Info.plist */; }; A81FFC7A28F15D9900B0FC7C /* HalmapApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = A81FFC7928F15D9900B0FC7C /* HalmapApp.swift */; }; A81FFC7C28F15D9900B0FC7C /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A81FFC7B28F15D9900B0FC7C /* ContentView.swift */; }; A81FFC7E28F15D9C00B0FC7C /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A81FFC7D28F15D9C00B0FC7C /* Assets.xcassets */; }; @@ -29,9 +30,6 @@ A81FFC9F28F180B200B0FC7C /* SongContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A81FFC9E28F180B200B0FC7C /* SongContentView.swift */; }; A81FFCA528F1B84000B0FC7C /* Halmap+Font.swift in Sources */ = {isa = PBXBuildFile; fileRef = A81FFCA428F1B84000B0FC7C /* Halmap+Font.swift */; }; A81FFCA728F1B84A00B0FC7C /* Halmap+Color.swift in Sources */ = {isa = PBXBuildFile; fileRef = A81FFCA628F1B84A00B0FC7C /* Halmap+Color.swift */; }; - A852F0282A46EDDA00078B47 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = A852F0272A46EDDA00078B47 /* GoogleService-Info.plist */; }; - A8A8260D2A4CE2D400F470DF /* PlayListRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8A8260C2A4CE2D400F470DF /* PlayListRow.swift */; }; - A8A8260F2A4CE37200F470DF /* PlayListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8A8260E2A4CE37200F470DF /* PlayListView.swift */; }; A8D1E92229169DAD00BF242C /* test-song.m4a in Resources */ = {isa = PBXBuildFile; fileRef = A8D1E92129169DAD00BF242C /* test-song.m4a */; }; A8F8BA9929C8CE5B00FCB229 /* AudioManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8F8BA9829C8CE5B00FCB229 /* AudioManager.swift */; }; B2699E0D28F1CB7E00267A4F /* Pretendard-Bold.otf in Resources */ = {isa = PBXBuildFile; fileRef = B2699E0C28F1CB7E00267A4F /* Pretendard-Bold.otf */; }; @@ -45,7 +43,6 @@ F939EBBC29D5920F005ED8CA /* OffsetModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = F939EBBB29D5920F005ED8CA /* OffsetModifier.swift */; }; F949284F28F2AFB800901F27 /* Music.json in Resources */ = {isa = PBXBuildFile; fileRef = F949284E28F2AFB800901F27 /* Music.json */; }; F95CE98B2917A8EF00FFE213 /* Settings.bundle in Resources */ = {isa = PBXBuildFile; fileRef = F95CE98A2917A8EF00FFE213 /* Settings.bundle */; }; - F95EF67C2A40695C00C6C3E7 /* HalfModalPresentationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F95EF67B2A40695C00C6C3E7 /* HalfModalPresentationController.swift */; }; F98F622929B4C9BD0025F50E /* Themes.swift in Sources */ = {isa = PBXBuildFile; fileRef = F98F622829B4C9BD0025F50E /* Themes.swift */; }; F98F622B29B4CB450025F50E /* ThemeManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = F98F622A29B4CB450025F50E /* ThemeManager.swift */; }; F98F623129B59CE60025F50E /* TeamName.swift in Sources */ = {isa = PBXBuildFile; fileRef = F98F623029B59CE60025F50E /* TeamName.swift */; }; @@ -66,6 +63,7 @@ 87F6ECF0291677AC004533C4 /* Halmap+UIApplication.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Halmap+UIApplication.swift"; sourceTree = ""; }; 87F6ECF42916B331004533C4 /* Halmap+UINavigationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Halmap+UINavigationController.swift"; sourceTree = ""; }; 87F6ECF62916BB44004533C4 /* Halmap+UIView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Halmap+UIView.swift"; sourceTree = ""; }; + A813C77329C779C400CF73FE /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; A81FFC7628F15D9900B0FC7C /* Halmap.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Halmap.app; sourceTree = BUILT_PRODUCTS_DIR; }; A81FFC7928F15D9900B0FC7C /* HalmapApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HalmapApp.swift; sourceTree = ""; }; A81FFC7B28F15D9900B0FC7C /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; @@ -81,9 +79,6 @@ A81FFC9E28F180B200B0FC7C /* SongContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SongContentView.swift; sourceTree = ""; }; A81FFCA428F1B84000B0FC7C /* Halmap+Font.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Halmap+Font.swift"; sourceTree = ""; }; A81FFCA628F1B84A00B0FC7C /* Halmap+Color.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Halmap+Color.swift"; sourceTree = ""; }; - A852F0272A46EDDA00078B47 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; - A8A8260C2A4CE2D400F470DF /* PlayListRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayListRow.swift; sourceTree = ""; }; - A8A8260E2A4CE37200F470DF /* PlayListView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PlayListView.swift; sourceTree = ""; }; A8D1E92129169DAD00BF242C /* test-song.m4a */ = {isa = PBXFileReference; lastKnownFileType = file; path = "test-song.m4a"; sourceTree = ""; }; A8F8BA9829C8CE5B00FCB229 /* AudioManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioManager.swift; sourceTree = ""; }; B2699E0B28F1CB5100267A4F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -98,7 +93,6 @@ F939EBBB29D5920F005ED8CA /* OffsetModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OffsetModifier.swift; sourceTree = ""; }; F949284E28F2AFB800901F27 /* Music.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = Music.json; sourceTree = ""; }; F95CE98A2917A8EF00FFE213 /* Settings.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = Settings.bundle; sourceTree = ""; }; - F95EF67B2A40695C00C6C3E7 /* HalfModalPresentationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HalfModalPresentationController.swift; sourceTree = ""; }; F98F622829B4C9BD0025F50E /* Themes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Themes.swift; sourceTree = ""; }; F98F622A29B4CB450025F50E /* ThemeManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThemeManager.swift; sourceTree = ""; }; F98F623029B59CE60025F50E /* TeamName.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TeamName.swift; sourceTree = ""; }; @@ -132,7 +126,7 @@ A81FFC6D28F15D9900B0FC7C = { isa = PBXGroup; children = ( - A852F0272A46EDDA00078B47 /* GoogleService-Info.plist */, + A813C77329C779C400CF73FE /* GoogleService-Info.plist */, B2699E0E28F1CB8800267A4F /* Font */, A81FFC7828F15D9900B0FC7C /* Halmap */, A81FFC7728F15D9900B0FC7C /* Products */, @@ -151,7 +145,6 @@ A81FFC7828F15D9900B0FC7C /* Halmap */ = { isa = PBXGroup; children = ( - A852F01A2A462F9300078B47 /* Utills */, A81FFC8E28F1770500B0FC7C /* View */, B2699E0B28F1CB5100267A4F /* Info.plist */, F98F622729B4C9A80025F50E /* Theme */, @@ -210,9 +203,8 @@ A81FFC9B28F1808700B0FC7C /* SongInformation */ = { isa = PBXGroup; children = ( - A8A826102A4CE5D900F470DF /* Lyric */, - A8A8260B2A4CE2C300F470DF /* PlayList */, A81FFC9C28F180A000B0FC7C /* SongPlayerView.swift */, + A81FFC9E28F180B200B0FC7C /* SongContentView.swift */, F9B3B7B629B842DD00232BB8 /* SongDetailView.swift */, ); path = SongInformation; @@ -230,31 +222,6 @@ path = Extensions; sourceTree = ""; }; - A852F01A2A462F9300078B47 /* Utills */ = { - isa = PBXGroup; - children = ( - F95EF67B2A40695C00C6C3E7 /* HalfModalPresentationController.swift */, - ); - path = Utills; - sourceTree = ""; - }; - A8A8260B2A4CE2C300F470DF /* PlayList */ = { - isa = PBXGroup; - children = ( - A8A8260E2A4CE37200F470DF /* PlayListView.swift */, - A8A8260C2A4CE2D400F470DF /* PlayListRow.swift */, - ); - path = PlayList; - sourceTree = ""; - }; - A8A826102A4CE5D900F470DF /* Lyric */ = { - isa = PBXGroup; - children = ( - A81FFC9E28F180B200B0FC7C /* SongContentView.swift */, - ); - path = Lyric; - sourceTree = ""; - }; B2699E0E28F1CB8800267A4F /* Font */ = { isa = PBXGroup; children = ( @@ -375,11 +342,11 @@ files = ( F95CE98B2917A8EF00FFE213 /* Settings.bundle in Resources */, F9A2DAA62A07DE9A008B84A9 /* Preview Assets.xcassets in Resources */, + A813C77429C779C400CF73FE /* GoogleService-Info.plist in Resources */, A81FFC8128F15D9C00B0FC7C /* Preview Assets.xcassets in Resources */, B2699E0D28F1CB7E00267A4F /* Pretendard-Bold.otf in Resources */, A81FFC7E28F15D9C00B0FC7C /* Assets.xcassets in Resources */, B2699E1028F1CB9700267A4F /* Pretendard-Medium.otf in Resources */, - A852F0282A46EDDA00078B47 /* GoogleService-Info.plist in Resources */, F949284F28F2AFB800901F27 /* Music.json in Resources */, A8D1E92229169DAD00BF242C /* test-song.m4a in Resources */, ); @@ -401,13 +368,10 @@ F939EBBC29D5920F005ED8CA /* OffsetModifier.swift in Sources */, F9A178222A1BF5CB003E8E4C /* HalfSheetView.swift in Sources */, 87F6ECF52916B331004533C4 /* Halmap+UINavigationController.swift in Sources */, - A8A8260D2A4CE2D400F470DF /* PlayListRow.swift in Sources */, - A8A8260F2A4CE37200F470DF /* PlayListView.swift in Sources */, A81FFCA528F1B84000B0FC7C /* Halmap+Font.swift in Sources */, F98F8D5D29D85B0C00F9C956 /* RequestSongView.swift in Sources */, A81FFC7C28F15D9900B0FC7C /* ContentView.swift in Sources */, A81FFC9D28F180A000B0FC7C /* SongPlayerView.swift in Sources */, - F95EF67C2A40695C00C6C3E7 /* HalfModalPresentationController.swift in Sources */, A81FFC8628F15D9C00B0FC7C /* Halmap.xcdatamodeld in Sources */, 87F6ECF72916BB44004533C4 /* Halmap+UIView.swift in Sources */, F91BE5FB29B18AF800F7E488 /* MainTabView.swift in Sources */, @@ -560,7 +524,7 @@ CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 5; DEVELOPMENT_ASSET_PATHS = "\"Halmap/Preview Content\""; - DEVELOPMENT_TEAM = 22A6RK2943; + DEVELOPMENT_TEAM = Y6P5ZFWBM8; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = Halmap/Info.plist; @@ -595,7 +559,7 @@ CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 5; DEVELOPMENT_ASSET_PATHS = "\"Halmap/Preview Content\""; - DEVELOPMENT_TEAM = 22A6RK2943; + DEVELOPMENT_TEAM = Y6P5ZFWBM8; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = Halmap/Info.plist; diff --git a/Halmap/Assets.xcassets/Image/2023/NC23.imageset/Contents.json b/Halmap/Assets.xcassets/Image/2023/NC23.imageset/Contents.json new file mode 100644 index 0000000..287fb9e --- /dev/null +++ b/Halmap/Assets.xcassets/Image/2023/NC23.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "NC23.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Halmap/Assets.xcassets/Image/2023/NC23.imageset/NC23.png b/Halmap/Assets.xcassets/Image/2023/NC23.imageset/NC23.png new file mode 100644 index 0000000..43f3442 Binary files /dev/null and b/Halmap/Assets.xcassets/Image/2023/NC23.imageset/NC23.png differ diff --git a/Halmap/Assets.xcassets/Image/Player/NCPlayer.imageset/Contents.json b/Halmap/Assets.xcassets/Image/Player/NCPlayer.imageset/Contents.json new file mode 100644 index 0000000..9c8ba38 --- /dev/null +++ b/Halmap/Assets.xcassets/Image/Player/NCPlayer.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "NCPlayer.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Halmap/Assets.xcassets/Image/Player/NCPlayer.imageset/NCPlayer.png b/Halmap/Assets.xcassets/Image/Player/NCPlayer.imageset/NCPlayer.png new file mode 100644 index 0000000..f3e0975 Binary files /dev/null and b/Halmap/Assets.xcassets/Image/Player/NCPlayer.imageset/NCPlayer.png differ diff --git "a/Halmap/Assets.xcassets/\355\214\200 \354\225\250\353\262\224 \354\273\244\353\262\204/NCAlbum.imageset/Contents.json" "b/Halmap/Assets.xcassets/\355\214\200 \354\225\250\353\262\224 \354\273\244\353\262\204/NCAlbum.imageset/Contents.json" index afd4eba..a0647a8 100644 --- "a/Halmap/Assets.xcassets/\355\214\200 \354\225\250\353\262\224 \354\273\244\353\262\204/NCAlbum.imageset/Contents.json" +++ "b/Halmap/Assets.xcassets/\355\214\200 \354\225\250\353\262\224 \354\273\244\353\262\204/NCAlbum.imageset/Contents.json" @@ -1,7 +1,7 @@ { "images" : [ { - "filename" : "NCAlbum.png", + "filename" : "NcAlbum.png", "idiom" : "universal", "scale" : "1x" }, diff --git a/Halmap/Data/DataManager.swift b/Halmap/Data/DataManager.swift index 0679ab9..96467da 100644 --- a/Halmap/Data/DataManager.swift +++ b/Halmap/Data/DataManager.swift @@ -20,8 +20,6 @@ class DataManager: ObservableObject { @Published var playerSongs: [Song] = [] @Published var teamSongs: [Song] = [] @Published var favoriteSongs = PersistenceController.shared.fetchFavoriteSong() - // 추가 - @Published var PlayListSongs = PersistenceController.shared.fetchPlayListSong() @Published var playerSongsAll = [[Song]](repeating: [], count: 10) @Published var teamSongsAll = [[Song]](repeating: [], count: 10) diff --git a/Halmap/Halmap.xcdatamodeld/Halmap.xcdatamodel/contents b/Halmap/Halmap.xcdatamodeld/Halmap.xcdatamodel/contents index 72239a9..8017e45 100644 --- a/Halmap/Halmap.xcdatamodeld/Halmap.xcdatamodel/contents +++ b/Halmap/Halmap.xcdatamodeld/Halmap.xcdatamodel/contents @@ -1,11 +1,10 @@ - + - diff --git a/Halmap/Persistence.swift b/Halmap/Persistence.swift index ca223b0..06e31bf 100644 --- a/Halmap/Persistence.swift +++ b/Halmap/Persistence.swift @@ -10,6 +10,7 @@ import SwiftUI struct PersistenceController { @AppStorage("selectedTeam") var selectedTeam = "Hanwha" + @FetchRequest(entity: CollectedSong.entity(), sortDescriptors: [NSSortDescriptor(keyPath: \CollectedSong.date, ascending: true)], animation: .default) private var collectedSongs: FetchedResults static let shared = PersistenceController() @@ -28,20 +29,9 @@ struct PersistenceController { container.viewContext.automaticallyMergesChangesFromParent = true } - func saveSongs(song: SongInfo, playListTitle: String?, menuType: MenuType, collectedSongs: FetchedResults) { + func saveSongs(song: SongInfo, playListTitle: String?) { let context = container.viewContext let collectedSong = CollectedSong(context: context) - - switch menuType { - case .cancelLiked: - break - case .playNext: - // TODO: 현재 곡 다음순서로 넣는 로직 필요 - collectedSong.order = Int64(collectedSongs.count) - case .playLast: - collectedSong.order = Int64(collectedSongs.count) - } - collectedSong.id = song.id collectedSong.title = song.title collectedSong.info = song.info @@ -51,7 +41,6 @@ struct PersistenceController { collectedSong.playListTitle = playListTitle collectedSong.team = song.team collectedSong.date = Date() - if context.hasChanges { do { @@ -63,39 +52,6 @@ struct PersistenceController { } } - func createCollectedSong(song: SongInfo, playListTitle: String?) -> CollectedSong { - let context = container.viewContext - let collectedSong = CollectedSong(context: context) - collectedSong.id = song.id - collectedSong.title = song.title - collectedSong.info = song.info - collectedSong.lyrics = song.lyrics - collectedSong.url = song.url - collectedSong.type = song.type - collectedSong.playListTitle = playListTitle - collectedSong.team = song.team - collectedSong.date = Date() - - print("collectedSong", collectedSong, song.title) - return collectedSong - } - - /// CollectedSong을 생성하기 위해 BufferList에 넣은 곡을 지우는 함수. - func resetBufferList(song: CollectedSong){ - - if song.playListTitle == "bufferPlayList" { - container.viewContext.delete(song) - } - - do { - try container.viewContext.save() - } catch { - container.viewContext.rollback() - print(error.localizedDescription) - } - } - - /// PlayList에서 곡을 지우는 함수. func deleteSongs(song: CollectedSong) { container.viewContext.delete(song) @@ -108,70 +64,6 @@ struct PersistenceController { } } - /// index를 이용하여 PlayListd에서 곡을 지우는 함수. - func deleteSong(at indexs: IndexSet, from results: FetchedResults) { - - for index in indexs { - let song = results[index] - container.viewContext.delete(song) - } - - do { - try container.viewContext.save() - } catch { - let nsError = error as NSError - fatalError("Unresolved error \(nsError), \(nsError.userInfo)") - } - } - - /// defaultPlayList 순서를 변경하는 함수 - func moveDefaultPlayListSong(from source: IndexSet, to destination: Int, based results: FetchedResults){ - - guard let itemToMove = source.first else { return } - - if itemToMove < destination{ - var startIndex = itemToMove + 1 - let endIndex = destination - 1 - var startOrder = results[itemToMove].order - while startIndex <= endIndex{ - results[startIndex].order = startOrder - startOrder = startOrder + 1 - startIndex = startIndex + 1 - } - results[itemToMove].order = startOrder - } - - else if destination < itemToMove{ - var startIndex = destination - let endIndex = itemToMove - 1 - var startOrder = results[destination].order + 1 - let newOrder = results[destination].order - while startIndex <= endIndex{ - results[startIndex].order = startOrder - startOrder = startOrder + 1 - startIndex = startIndex + 1 - } - results[itemToMove].order = newOrder - } - - do{ - try container.viewContext.save() - } - catch{ - print(error.localizedDescription) - } - } - - /// defaultPlayList 앞에 추가하는 함수 - func appendDefaultPlayListSong(song: SongInfo){ - - } - /// defaultPlayList 뒤에 추가하는 함수 - func pushBackDefaultPlayListSong(song: SongInfo){ - - } - - func fetchFavoriteSong() -> [CollectedSong] { let fetchRequest: NSFetchRequest = CollectedSong.fetchRequest() @@ -182,26 +74,7 @@ struct PersistenceController { } } - func findFavoriteSong(song: Song, collectedSongs: FetchedResults) -> CollectedSong { - if let index = collectedSongs.firstIndex(where: {song.id == $0.id}) { - return collectedSongs[index] - } else { - return CollectedSong() - } - } - - func fetchPlayListSong() -> [CollectedSong] { - let fetchRequest: NSFetchRequest = CollectedSong.fetchRequest() - - do{ - return try container.viewContext.fetch(fetchRequest) - }catch { - return [] - } - } - - - func findPlayListSong(song: Song, collectedSongs: FetchedResults) -> CollectedSong { + func findFavoriteSong(song: Song) -> CollectedSong { if let index = collectedSongs.firstIndex(where: {song.id == $0.id}) { return collectedSongs[index] } else { diff --git a/Halmap/Utills/HalfModalPresentationController.swift b/Halmap/Utills/HalfModalPresentationController.swift deleted file mode 100644 index ae80719..0000000 --- a/Halmap/Utills/HalfModalPresentationController.swift +++ /dev/null @@ -1,36 +0,0 @@ -// -// HalfModalPresentationController.swift -// Halmap -// -// Created by JeonJimin on 2023/06/19. -// - -import SwiftUI - -class HalfSheetController: UIHostingController where Content : View { - override func viewWillAppear(_ animated: Bool) { - super.viewWillAppear(animated) - - if let presentation = sheetPresentationController { - presentation.detents = [.medium()] - presentation.prefersGrabberVisible = true - presentation.largestUndimmedDetentIdentifier = .none - } - } -} - -struct HalfSheet: UIViewControllerRepresentable where Content : View { - - private let content: Content - - @inlinable init(@ViewBuilder content: () -> Content) { - self.content = content() - } - - func makeUIViewController(context: Context) -> HalfSheetController { - return HalfSheetController(rootView: content) - } - - func updateUIViewController(_: HalfSheetController, context: Context) { - } -} diff --git a/Halmap/View/HalfSheetView.swift b/Halmap/View/HalfSheetView.swift index dd21802..557138c 100644 --- a/Halmap/View/HalfSheetView.swift +++ b/Halmap/View/HalfSheetView.swift @@ -10,41 +10,40 @@ import SwiftUI struct HalfSheetView: View { @Environment(\.managedObjectContext) var managedObjectContext let persistence = PersistenceController.shared + @ObservedObject var collectedSong: CollectedSong + @State var songData: Song + @State var song: CollectedSong = CollectedSong() + var team: String @Binding var showSheet: Bool - @Binding var collectedSongData: CollectedSong? + var body: some View { VStack(spacing: 0) { - // Capsule() - // .fill(Color.gray) - // .frame(width: 35, height: 5) - // .padding(.vertical, 18) - if let collectedSongData { - HStack(spacing: 16) { - Image(collectedSongData.type ? "\(collectedSongData.team ?? "")Album" : "\(collectedSongData.team ?? "")Player") - .resizable() - .frame(width: 52, height: 52) - .cornerRadius(10) - .shadow(color: .black.opacity(0.25), radius: 4, x: 0, y: 4) - VStack(alignment: .leading, spacing: 6) { - Text(collectedSongData.title ?? "") - .font(Font.Halmap.CustomBodyBold) - .foregroundColor(.customBlack) - Text(TeamName(rawValue: collectedSongData.team ?? "" )?.fetchTeamNameKr() ?? ".") - .font(Font.Halmap.CustomCaptionMedium) - .foregroundColor(.customDarkGray) - } - .frame(maxWidth: .infinity, alignment: .leading) + HStack(spacing: 16) { + Image("\(team)SongListImage") + .frame(width: 52, height: 52) + .shadow(color: .black.opacity(0.25), radius: 4, x: 0, y: 4) + VStack(alignment: .leading, spacing: 6) { + Text(songData.title ) + .font(Font.Halmap.CustomBodyBold) + .foregroundColor(.customBlack) + Text(TeamName(rawValue: team )?.fetchTeamNameKr() ?? ".") + .font(Font.Halmap.CustomCaptionMedium) + .foregroundColor(.customDarkGray) } - .padding(.horizontal, 20) - .padding(.top, 27) - Divider() - .overlay(Color.customGray.opacity(0.6)) - .padding(EdgeInsets(top: 20, leading: 0, bottom: 29, trailing: 0)) - MenuItem(menuType: .cancelLiked, collectedSong: collectedSongData, showSheet: $showSheet) - MenuItem(menuType: .playNext, collectedSong: collectedSongData, showSheet: $showSheet) - MenuItem(menuType: .playLast, collectedSong: collectedSongData, showSheet: $showSheet) - Spacer() + .frame(maxWidth: .infinity, alignment: .leading) } + .padding(.horizontal, 20) + .padding(.top, 27) + Divider() + .overlay(Color.customGray.opacity(0.6)) + .padding(EdgeInsets(top: 20, leading: 0, bottom: 29, trailing: 0)) + MenuItem(menuType: .cancelLiked, collectedSong: collectedSong, showSheet: $showSheet) + MenuItem(menuType: .playNext, collectedSong: collectedSong, showSheet: $showSheet) + MenuItem(menuType: .playLast, collectedSong: collectedSong, showSheet: $showSheet) + Spacer() + } + .onAppear() { + print("****\(songData)") } } } @@ -54,12 +53,9 @@ struct MenuItem: View { @ObservedObject var collectedSong: CollectedSong @Binding var showSheet: Bool - let persistence = PersistenceController.shared - @FetchRequest(entity: CollectedSong.entity(), sortDescriptors: [NSSortDescriptor(keyPath: \CollectedSong.order, ascending: true)], predicate: PlayListFilter(filter: "defaultPlayList").predicate, animation: .default) private var defaultPlayListSongs: FetchedResults - var body: some View { Button { - menuType.fetchFunction(collectedSong: collectedSong, collectedSongs: defaultPlayListSongs) + menuType.fetchFunction(collectedSong: collectedSong) showSheet.toggle() } label: { HStack(spacing: 24) { @@ -102,26 +98,17 @@ enum MenuType { } } - func fetchFunction(collectedSong: CollectedSong, collectedSongs: FetchedResults) { + func fetchFunction(collectedSong: CollectedSong) { let persistence = PersistenceController.shared switch self { case .cancelLiked: return persistence.deleteSongs(song: collectedSong) case .playNext: //TODO: 바로 다음에 재생 기능 추가 - let song = collectedSong - let songInfo = SongInfo(id: song.id ?? "", team: song.team ?? "", type: song.type, title: song.title ?? "", lyrics: song.lyrics ?? "", info: song.info ?? "", url: song.url ?? "") - persistence.resetBufferList(song: collectedSong) - - return persistence.saveSongs(song: songInfo, playListTitle: "defaultPlayList", menuType: .playNext, collectedSongs: collectedSongs) - + return (print("play next")) case .playLast: //TODO: 맨 마지막에 재생 기능 추가 - let song = collectedSong - let songInfo = SongInfo(id: song.id ?? "", team: song.team ?? "", type: song.type, title: song.title ?? "", lyrics: song.lyrics ?? "", info: song.info ?? "", url: song.url ?? "") - persistence.resetBufferList(song: collectedSong) - - return persistence.saveSongs(song: songInfo, playListTitle: "defaultPlayList", menuType: .playLast, collectedSongs: collectedSongs) + return (print("play last")) } } } diff --git a/Halmap/View/MainSongListTabView.swift b/Halmap/View/MainSongListTabView.swift index 0d7173b..4b8325b 100644 --- a/Halmap/View/MainSongListTabView.swift +++ b/Halmap/View/MainSongListTabView.swift @@ -10,24 +10,13 @@ import SwiftUI struct MainSongListTabView: View { @AppStorage("selectedTeam") var selectedTeam = "Hanwha" - // @EnvironmentObject var dataManager: DataManager - @Environment(\.managedObjectContext) private var viewContext -// @FetchRequest(entity: CollectedSong.entity(), sortDescriptors: [NSSortDescriptor(keyPath: \CollectedSong.date, ascending: true)], predicate: PlayListFilter(filter: "playList").predicate, animation: .default) private var collectedSongs: FetchedResults @State private var showingTeamChaingView: Bool = false @State var index = 0 // SongInformationView - @State private var isShowingFullScreenCover = false - // HalfSheetModalView - @State private var isShowingSheet = false - @State private var isActivateNavigationLink = false - @Environment(\.dismiss) private var sheetDismiss - @Environment(\.dismiss) private var navigationLinkDismiss - - let persistence = PersistenceController.shared - @State var collectedSong: CollectedSong? + @State private var showingFullScreenCover = false init() { Color.setColor(selectedTeam) @@ -54,11 +43,8 @@ struct MainSongListTabView: View { .padding(.horizontal, 20) TabView(selection: $index) { - - // MARK: 팀 응원가 탭 List { ForEach(dataManager.teamSongs) { song in - let music = Song(id: song.id, type: song.type, title: song.title, @@ -73,8 +59,7 @@ struct MainSongListTabView: View { info: song.info, url: song.url) - ZStack() { - // Stack 1: Contents + NavigationLink(destination: SongDetailView(song: music, team: selectedTeam)) { HStack(spacing: 16) { Image(dataManager.checkSeasonSong(data: songInfo) ? "\(selectedTeam)23" : "\(selectedTeam)Album") .resizable() @@ -91,42 +76,12 @@ struct MainSongListTabView: View { } .frame(maxWidth: .infinity, alignment: .leading) .lineLimit(1) - }.background( NavigationLink("", destination: SongDetailView(song: music, team: selectedTeam), isActive: $isActivateNavigationLink ).opacity(0) ) - .onTapGesture { - isActivateNavigationLink = true - isShowingSheet = false - navigationLinkDismiss() - } - - // Stack 2: Button - HStack { - Spacer() - VStack(alignment: .trailing){ - Button { - print("버튼") - isShowingSheet = true - isActivateNavigationLink = false - sheetDismiss() - - collectedSong = persistence.createCollectedSong(song: songInfo, playListTitle: "bufferPlayList") - - - } label: { - Image(systemName: "ellipsis").foregroundColor(.customDarkGray) - } - .sheet(isPresented: $isShowingSheet) { - HalfSheet{ - HalfSheetView(showSheet: $isShowingSheet, collectedSongData: $collectedSong) - } - } - }.frame(width: 20, height: 20) } } } .listRowInsets(EdgeInsets(top: 15, leading: 0, bottom: 15, trailing: 0)) .listRowBackground(Color.systemBackground) .listRowSeparatorTint(Color.customGray) - RequestSongView(buttonColor: Color.HalmacPoint) .listRowInsets(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0)) .listRowBackground(Color.systemBackground) @@ -136,8 +91,6 @@ struct MainSongListTabView: View { .listStyle(.plain) .tag(0) - - // MARK: 선수 응원가 탭 List { ForEach(dataManager.playerSongs) { song in let music = Song(id: song.id, @@ -154,10 +107,9 @@ struct MainSongListTabView: View { lyrics: song.lyrics, info: song.info, url: song.url) - - ZStack{ - // Stack 1: Contents + + NavigationLink(destination: SongDetailView(song: music, team: selectedTeam)) { HStack(spacing: 16) { Image(dataManager.checkSeasonSong(data: songInfo) ? "\(selectedTeam)23" : "\(selectedTeam)Player") .resizable() @@ -174,37 +126,6 @@ struct MainSongListTabView: View { } .frame(maxWidth: .infinity, alignment: .leading) .lineLimit(1) - }.background( NavigationLink("", destination: SongDetailView(song: music, team: selectedTeam), isActive: $isActivateNavigationLink ).opacity(0) ) - .onTapGesture { - isActivateNavigationLink = true - isShowingSheet = false - navigationLinkDismiss() - } - - - - - // Stack 2: Button - HStack { - Spacer() - VStack(alignment: .trailing){ - Button { - print("버튼") - isShowingSheet = true - isActivateNavigationLink = false - sheetDismiss() - - collectedSong = persistence.createCollectedSong(song: songInfo, playListTitle: "bufferPlayList") - - } label: { - Image(systemName: "ellipsis").foregroundColor(.customDarkGray) - } - .sheet(isPresented: $isShowingSheet) { - HalfSheet{ - HalfSheetView(showSheet: $isShowingSheet, collectedSongData: $collectedSong) - } - } - }.frame(width: 20, height: 20) } } } diff --git a/Halmap/View/ScalingHeaderView.swift b/Halmap/View/ScalingHeaderView.swift index 534f400..e7ba822 100644 --- a/Halmap/View/ScalingHeaderView.swift +++ b/Halmap/View/ScalingHeaderView.swift @@ -9,16 +9,12 @@ import SwiftUI struct ScalingHeaderView: View { @EnvironmentObject var dataManager: DataManager - let maxHeight: CGFloat = UIScreen.getHeight(216) + let maxHeight: CGFloat = 216 var topEdge: CGFloat @State var offset: CGFloat = 0 - @State var isShowSheet = false - @State var collectedSongData: CollectedSong? - @State var isDraged = false @FetchRequest(entity: CollectedSong.entity(), sortDescriptors: [NSSortDescriptor(keyPath: \CollectedSong.date, ascending: true)], predicate: PlayListFilter(filter: "favorite").predicate, animation: .default) private var collectedSongs: FetchedResults - let persistence = PersistenceController.shared var body: some View { @@ -26,43 +22,28 @@ struct ScalingHeaderView: View { VStack(spacing: 15) { GeometryReader { proxy in VStack(spacing: 0) { - topBar + TopBar(topEdge: topEdge, offset: $offset) .frame(maxWidth: .infinity) .frame(height: getHeaderHeight(), alignment: .bottom) .overlay( topTitle - .opacity(checkScrollRequirement(listCount: collectedSongs.count) && isDraged ? 1 : 0) + .opacity(topTitleOpacity()) ) HStack() { Text("총 \(collectedSongs.count)곡") .font(Font.Halmap.CustomCaptionBold) .foregroundColor(.customDarkGray) Spacer() - Button { - withAnimation { - isDraged.toggle() - } - } label: { - HStack(spacing: 5) { - Image(systemName: "play.circle.fill") - .foregroundColor(.mainGreen) - .font(.system(size: 20)) - Text("전체 재생하기") - .font(Font.Halmap.CustomCaptionBold) - .foregroundColor(.mainGreen) - } - .opacity(checkScrollRequirement(listCount: collectedSongs.count) && isDraged ? 1 : 0) - } } .padding(.horizontal, 20) - .padding(.vertical, UIScreen.getHeight(17)) + .padding(.vertical, 17) Divider() .overlay(Color.customGray.opacity(0.6)) .padding(.horizontal, 20) } .background(Color.systemBackground) } - .frame(height: maxHeight + UIScreen.getHeight(48)) + .frame(height: maxHeight + 48) .offset(y: -offset) .zIndex(1) @@ -74,8 +55,8 @@ struct ScalingHeaderView: View { .frame(width: 200, height: 200) .padding(.top, 60) } else { - ForEach(self.collectedSongs, id: \.self) { favoriteSong in - var song = Song( + ForEach(collectedSongs) { favoriteSong in + let song = Song( id: favoriteSong.id ?? "", type: favoriteSong.type , title: favoriteSong.title ?? "" , @@ -114,21 +95,10 @@ struct ScalingHeaderView: View { .lineLimit(1) Button { - print("scaling favorite",favoriteSong) - self.collectedSongData = favoriteSong - - song = Song(id: favoriteSong.id ?? "", type: favoriteSong.type, title: favoriteSong.title ?? "", lyrics: favoriteSong.lyrics ?? "", info: favoriteSong.info ?? "", url: favoriteSong.url ?? "") - - isShowSheet.toggle() - + persistence.deleteSongs(song: favoriteSong) } label: { - Image(systemName: "ellipsis") - .foregroundColor(.black.opacity(0.2)) - } - .sheet(isPresented: $isShowSheet) { - HalfSheet { - HalfSheetView(showSheet: $isShowSheet, collectedSongData: $collectedSongData) - } + Image(systemName: "heart.fill") + .foregroundColor(.mainGreen) } } .padding(.horizontal, 20) @@ -141,48 +111,10 @@ struct ScalingHeaderView: View { } } .modifier(OffsetModifier(offset: $offset)) - .onChange(of: offset) { _ in - if offset > -UIScreen.getHeight(90) { - withAnimation { - isDraged = false - } - } else if offset < 0 { - withAnimation { - isDraged = true - } - } - } } .coordinateSpace(name: "StorageScroll") .background(Color.systemBackground) - } - - var topBar: some View { - ZStack(alignment: .bottom) { - Image("storageTop") - .resizable() - HStack { - Text("보관함") - .font(Font.Halmap.CustomLargeTitle) - Spacer() - Button { - withAnimation { - isDraged.toggle() - } - } label: { - Image(systemName: "play.circle.fill") - .foregroundColor(.mainGreen) - .font(.system(size: 50)) - } - } - .padding(EdgeInsets(top: 0, leading: 20, bottom: 20, trailing: 20)) - } - .opacity(checkScrollRequirement(listCount: collectedSongs.count) && isDraged ? 0 : 1) - .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .bottom) - .background(Color.systemBackground) - } - var topTitle: some View { VStack{ HStack { @@ -197,11 +129,45 @@ struct ScalingHeaderView: View { } func getHeaderHeight() -> CGFloat { - isDraged ? (59 + topEdge) : maxHeight + offset + let topHeight = maxHeight + offset + + return topHeight >= (59 + topEdge) ? topHeight : (59 + topEdge) + } + func topTitleOpacity() -> CGFloat { + let progress = -offset*2 / (maxHeight - (59 + topEdge)) + return progress + } + func getOpacity() -> CGFloat { + let progress = -offset*2 / 40 + let opacity = 1 - progress + return offset < 0 ? opacity : 1 } +} + +struct TopBar: View { + + let topEdge: CGFloat + @Binding var offset: CGFloat - func checkScrollRequirement(listCount: Int) -> Bool{ - UIScreen.screenHeight - (59 + topEdge) - CGFloat(75 * listCount) <= 0 ? true : false + var body: some View { + ZStack(alignment: .bottom) { + Image("storageTop") + .resizable() + HStack { + Text("보관함") + .font(Font.Halmap.CustomLargeTitle) + Spacer() + } + .padding(EdgeInsets(top: 0, leading: 20, bottom: 20, trailing: 20)) + } + .opacity(getOpacity()) + .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .bottom) + .background(Color.systemBackground) + } + func getOpacity() -> CGFloat { + let progress = -offset / 40 + let opacity = 1 - progress + return offset < 0 ? opacity : 1 } } diff --git a/Halmap/View/SongInformation/PlayList/PlayListRow.swift b/Halmap/View/SongInformation/PlayList/PlayListRow.swift deleted file mode 100644 index 0d09c39..0000000 --- a/Halmap/View/SongInformation/PlayList/PlayListRow.swift +++ /dev/null @@ -1,37 +0,0 @@ -// -// PlayListRow.swift -// Halmap -// -// Created by Yeni Hwang on 2023/06/29. -// - -import SwiftUI - -struct PlayListRow: View { - @EnvironmentObject var dataManager: DataManager - @State var songInfo: SongInfo - - var body: some View { - HStack{ - Image(dataManager.checkSeasonSong(data: songInfo) ? "\(songInfo.team ?? "")23" : "\( songInfo.team ?? "NC")\(songInfo.type ? "Player" : "Album")") - .resizable() - .frame(width: 40, height: 40) - .cornerRadius(8) - VStack(alignment: .leading, spacing: 8) { - Text(songInfo.title ?? "test ") - .font(Font.Halmap.CustomBodyMedium) - .foregroundColor(.white) - Text(TeamName(rawValue: songInfo.team ?? "NC")?.fetchTeamNameKr() ?? ".") - .font(Font.Halmap.CustomCaptionMedium) - .foregroundColor(.customDarkGray) - }.padding(.horizontal, 16) - .frame(maxWidth: .infinity, alignment: .leading) - .lineLimit(1) - Image(systemName: "text.justify") - .frame(width: 18, height: 18) - .foregroundStyle(.gray) - } - .padding(.horizontal, 20) // 40 -> 20 값 조정 - .frame(height: 50) // 70 -> 50값 조정 - } -} diff --git a/Halmap/View/SongInformation/PlayList/PlayListView.swift b/Halmap/View/SongInformation/PlayList/PlayListView.swift deleted file mode 100644 index 39d1ea3..0000000 --- a/Halmap/View/SongInformation/PlayList/PlayListView.swift +++ /dev/null @@ -1,95 +0,0 @@ -// -// PlayListView.swift -// Halmap -// -// Created by Yeni Hwang on 2023/06/24. -// - -import SwiftUI - -struct PlayListView: View { - - - @EnvironmentObject var audioManager: AudioManager - @AppStorage("selectedTeam") var selectedTeam = "Hanwha" - @EnvironmentObject var dataManager: DataManager - @Binding var song: Song - - let persistence = PersistenceController.shared - @FetchRequest(entity: CollectedSong.entity(), sortDescriptors: [NSSortDescriptor(keyPath: \CollectedSong.order, ascending: true)], predicate: PlayListFilter(filter: "defaultPlayList").predicate, animation: .default) private var collectedSongs: FetchedResults - - @FetchRequest(entity: CollectedSong.entity(), - sortDescriptors: [ - NSSortDescriptor(keyPath: \CollectedSong.id, ascending: true) - ], animation: .default - ) private var playListSongs: FetchedResults - - var body: some View { - - ZStack{ - if collectedSongs.count != 0 { - List { - ForEach(collectedSongs, id: \.self) { playListSong in - - let song = Song( - id: playListSong.id ?? "", - type: playListSong.type , - title: playListSong.title ?? "" , - lyrics: playListSong.lyrics ?? "", - info: playListSong.info ?? "", - url: playListSong.url ?? "" - ) - - let songInfo = SongInfo( - id: playListSong.id ?? "", - team: playListSong.team ?? "", - type: playListSong.type , - title: playListSong.title ?? "" , - lyrics: playListSong.lyrics ?? "", - info: playListSong.info ?? "", - url: playListSong.url ?? "" - ) - - PlayListRow(songInfo: songInfo) - .onTapGesture { - self.song = song - audioManager.removePlayer() - audioManager.AMset(song: song, selectedTeam: selectedTeam) - } - - - }.onDelete { indexSet in - persistence.deleteSong(at: indexSet, from: collectedSongs) - }.onMove { indexSet, destination in - persistence.moveDefaultPlayListSong(from: indexSet, to: destination, based: collectedSongs) - } - .listRowBackground(Color.clear) - } - .listStyle(.plain) - .modifier(ListBackgroundModifier()) - } - - }.background(Color("\(selectedTeam)Sub")) - } - - func findPlayListSong(at offsets: IndexSet) -> CollectedSong { - if let index = playListSongs.firstIndex(where: {song.id == $0.id}) { - return playListSongs[index] - } else { - return CollectedSong() - } - } -} - -struct ListBackgroundModifier: ViewModifier { - - @ViewBuilder - func body(content: Content) -> some View { - if #available(iOS 16.0, *) { - content - .scrollContentBackground(.hidden) - } else { - content - } - } -} diff --git a/Halmap/View/SongInformation/Lyric/SongContentView.swift b/Halmap/View/SongInformation/SongContentView.swift similarity index 100% rename from Halmap/View/SongInformation/Lyric/SongContentView.swift rename to Halmap/View/SongInformation/SongContentView.swift diff --git a/Halmap/View/SongInformation/SongDetailView.swift b/Halmap/View/SongInformation/SongDetailView.swift index 3a50a42..4b822a8 100644 --- a/Halmap/View/SongInformation/SongDetailView.swift +++ b/Halmap/View/SongInformation/SongDetailView.swift @@ -16,7 +16,6 @@ struct SongDetailView: View { @State var song: Song @State var team: String - @State var isPlayListView = false @State var isScrolled = false @State var isFavorite = false @@ -31,14 +30,7 @@ struct SongDetailView: View { Color("\(team)Sub") .ignoresSafeArea() - if isPlayListView { - VStack{ - Rectangle().foregroundColor(.clear).frame(height: 10) - PlayListView(song: $song) - } - } else { - SongContentView(song: $song, team: $team, isScrolled: $isScrolled) - } + SongContentView(song: $song, team: $team, isScrolled: $isScrolled) VStack(spacing: 0) { Rectangle() @@ -51,28 +43,10 @@ struct SongDetailView: View { .foregroundColor(Color(UIColor.clear)) } Spacer() - - ZStack{ - Rectangle() - .frame(height: 120) - .background(Color.fetchBottomGradient(color: Color("\(team)Sub"))) - .foregroundColor(Color(UIColor.clear)) - - // PlayListButton - HStack(){ - Spacer() - Button(action: { - isPlayListView.toggle() - }, label: { - ZStack { - Circle().foregroundColor(Color("\(team)Background")).frame(width: 43, height: 43) - Image(systemName: isPlayListView ? "quote.bubble.fill" : "list.bullet").foregroundColor(.white) - - } - }) - }.padding(20) - } - + Rectangle() + .frame(height: 120) + .background(Color.fetchBottomGradient(color: Color("\(team)Sub"))) + .foregroundColor(Color(UIColor.clear)) ZStack(alignment: .center) { Rectangle() .frame(height: UIScreen.getHeight(120)) @@ -91,7 +65,7 @@ struct SongDetailView: View { persistence.deleteSongs(song: findFavoriteSong()) } else { let songInfo = SongInfo(id: song.id, team: team, type: song.type, title: song.title, lyrics: song.lyrics, info: song.info, url: song.url) - persistence.saveSongs(song: songInfo, playListTitle: "favorite", menuType: .cancelLiked, collectedSongs: favoriteSongs) + persistence.saveSongs(song: songInfo, playListTitle: "favorite") } isFavorite.toggle() } label: {