如何在Swift/SwiftUI中创建专辑追踪器的逻辑

wko9yo5t  于 2023-02-11  发布在  Swift
关注(0)|答案(1)|浏览(90)

我正在创建一个应用程序与轨道和使用的API。

// Example of an album
 {
          "title": "Relaxing Music Album 1",
          "img": "...",
          "shortDescription": "...",
          "fullDescription": "...",
          "soungs": [
            {
              "img": "activity.jpg",
              "name": "Relaxing Music Vol1",
              "performer": "RelaxingTime",
              "linkSoung": "relaxing-music-vol1.mp3"
            },
            {
              "img": "...",
              "name": "...",
              "performer": "...",
              "linkSoung": "..."
            },
            {
              "img": "...",
              "name": "...",
              "performer": "...",
              "linkSoung": "..."
            },
            {
              "img": "...",
              "name": "...",
              "performer": "...",
              "linkSoung": "..."
            }
          ]
        }

有专辑,其中包含轨道。您可以打开每个轨道单独的帮助下ForEach。

ForEach(data.soungs ?? [], id: \.name) { songs in
                    
                    SongAlbumRowView(data: songs) // Sends data about the song (name, url etc.)

                    Divider()
                }

我有一个跟踪器的用户界面,显示所有曲目的计数:

ProgressBar(sessionsCount: data.soungs!.count, playedAudioCount: 0) // playedAudioCount must be += 1 each time when I open new track of the album.

我如何能修改我的视图/计数器/ForEach在一个特定的方式,每次我打开这一特定专辑的新歌曲(打开就行了,不想写的太难)计数器加1,直到playedAudioCount == data.soungs!. count,我没有足够的技能来创建这个函数,所以我在这里寻求帮助,我到处寻找这个问题的答案,所以,如果我的职位是重复的,请提供一个链接到旧职位。谢谢。
连接SongAlbumView以防万一:

struct SongAlbumRowView: View {
    
    var isPremiumBought: Bool = true
    let data: Soung
    
    var body: some View {
        
        NavigationLink {
            PlayerView(data: data, track: "url" + (data.linkSoung ?? ""))
        } label: {
            HStack {
                
                    ZStack {
                        
                        Rectangle()
                            .fill(.white)
                            .frame(width: 24, height: 24)
                            .cornerRadius(8)
                            
                        if isPremiumBought {
                            Image(systemName: "play.fill")
                                .font(.system(size: 14))
                                .foregroundColor(Color("active"))
                        } else {
                            Image(systemName: "lock")
                                .font(.system(size: 14))
                                .foregroundColor(Color("inactive"))
                        }
                    }
                    .padding(.leading)
                    
                Text(data.name!)
                            .font(.custom("Manrope-Bold", size: 14))
                            .foregroundColor(.white)
                            .multilineTextAlignment(.leading)
                        
                        Spacer()
                        
                        Text("13MIN")
                            .font(.custom("Manrope-Medium", size: 12))
                            .foregroundColor(.white)
                            .padding(.trailing)
                
            }
            .frame(height: 52)
        }
    }
}
bakd9h0s

bakd9h0s1#

这里有一个你想要达到的目标的功能性例子。

我已经做了必要的最低限度的修改,并提供有关文件供您参考。
data.json

{
 "title": "Relaxing Music Album 1",
 "img": "...",
 "shortDescription": "...",
 "fullDescription": "...",
 "songs": [
   {
     "img": "activity.jpg",
     "name": "Relaxing Music Vol1",
     "performer": "RelaxingTime",
     "linkSong": "relaxing-music-vol1.mp3"
   },
   {
     "img": "...",
     "name": "Relaxing Music Vol2",
     "performer": "...",
     "linkSong": "..."
   },
   {
     "img": "...",
     "name": "Relaxing Music Vol3",
     "performer": "...",
     "linkSong": "..."
   },
   {
     "img": "...",
     "name": "Relaxing Music Vol4",
     "performer": "...",
     "linkSong": "..."
   }
 ]
}

Album.swift

struct Album: Decodable {
    let title: String
    let img: String
    let shortDescription: String
    let fullDescription: String
    let songs: [Album.Song]
    
    struct Song: Decodable {
        let img: String
        let name: String
        let performer: String
        let linkSong: String
    }
}

ViewModel.swift

enum DecodeError: Error {
    case invalidInput
}

@MainActor
class ViewModel: ObservableObject {
    @Published var data: Album? = nil
    
    func fetchData() async throws {
        guard let url = Bundle.main.url(forResource: "data", withExtension: "json") else {
            throw DecodeError.invalidInput
        }
        let rawData = try Data(contentsOf: url)
        let album = try JSONDecoder().decode(Album.self, from: rawData)
        data =  album
    }
}

View.swift

struct ParentView: View {
    @EnvironmentObject var viewModel: ViewModel
    var body: some View {
        AlbumView(songs: viewModel.data?.songs ?? [])
        .onAppear {
            Task {
                do {
                    try await viewModel.fetchData()
                } catch {
                    print(error)
                }
            }
        }
    }
}

struct AlbumView: View {
    var songs: [Album.Song]
    @State private var bag: Set<String> = .init()
    var body: some View {
        VStack {
            ProgressView(value: Double(bag.count) / Double(songs.count))
            ForEach(songs, id: \.name) { songs in
                SongAlbumRowView(data: songs, bag: $bag)
                Divider()
            }
        }.padding()
    }
}

struct SongAlbumRowView: View {
    var isPremiumBought: Bool = true
    let data: Album.Song
    @Binding var bag: Set<String>
    
    var body: some View {
        NavigationLink(destination: PlayerView(data: data, track: "url" + (data.linkSoung ?? ""))) {
            HStack {
                ZStack {
                    Rectangle().fill(.white).frame(width: 24, height: 24).cornerRadius(8)
                    if isPremiumBought {
                        Image(systemName: "play.fill").font(.system(size: 14)).foregroundColor(.green)
                    } else {
                        Image(systemName: "lock").font(.system(size: 14)).foregroundColor(.gray)
                    }
                }.padding(.leading)
                Text(data.name).font(.custom("Manrope-Bold", size: 14)).foregroundColor(.black).multilineTextAlignment(.leading)
                Spacer()
                Text("13MIN").font(.custom("Manrope-Medium", size: 12)).foregroundColor(.white).padding(.trailing)
                }.frame(height: 52)
        }.simultaneousGesture(TapGesture().onEnded{
            if !bag.contains(data.name) { bag.insert(data.name) }
        })
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ParentView().environmentObject(ViewModel())
    }
}

相关问题