首页 > 解决方案 > 从多个图层制作收藏按钮并使用 EnvironmentObject 绑定两个列表

问题描述

通过在我的歌曲应用程序上添加收藏夹,我被困了好几天。在这里,我想在我的歌曲列表中添加一个收藏按钮。我希望我最喜欢的按钮对应于父视图和详细视图。例如,如果我在父视图中将我的一首歌曲标记为(心形按钮)为收藏,我还想在我的详细视图中显示该歌曲被标记为收藏。如果我再次从我的详细视图中按下最喜欢的按钮(这将变得不喜欢),我还想表明我父母的视图中也禁用了最喜欢的歌曲。所以有两个按钮可以控制歌曲是喜欢还是不喜欢。根据我的基本理解,我需要使用 EnvironmentObject 来绑定父视图和子(或细节)视图,因为这两个视图都分为三到四个视图层。根据我的代码,每次我点击收藏夹时,所有歌曲都被选中收藏,我不明白如何在这两个视图之间传递数据。我在下面编写了所有代码用于测试目的。它们有点乱,但我希望它们是可测试的。提前致谢。

import Foundation
import SwiftUI

struct Lyric: Identifiable {

    let id = UUID()
    let number: Int
    var zoTitle: String
    let engTitle: String
    let key: String
    let musicStyle: String
    let translation: String
    let verse1: String
    var verse2: String?
    var verse3: String?
    var verse4: String?
    var verse5: String?
    var preChorus: String?
    var chorus: String?
    var bridge: String?
    var isFavoriteSong: Bool
    let isNotedSong: Bool
    let isHighlightedSong: Bool
    let isBookMarkedSong: Bool
    let isSharedSong: Bool
    let isCopySong: Bool
}

public struct LyricList {
    
    static var hymnLa = [
        
        Lyric(number: 1,
              
            zoTitle: "Pasian Phatna",
            
            engTitle: "Praise God, from whom All Blessings Flow",
            
            key: "Key: G",
            
            musicStyle: "",
            
            translation: "S.S. 9",
            
            verse1: "Thupha kheempeuh hong pia Pasian\nPhat un, leitung mi khempeuh aw, \nPhat un vantung mi honpi'n zong; \nPhat un Pa, Ta le Kha Siangtho.",
            
            isFavoriteSong: false,
            isNotedSong: false,
            isHighlightedSong: false,
            isBookMarkedSong: false,
            isSharedSong: false,
            isCopySong: false),
            
            
            
        Lyric(number: 2,
              
            zoTitle: "Itna Kumpi, Ka Tuu-Cing Pa",
            
            engTitle: "The King of Love My Shephaerd Is (Dominus Regit Me)",
            
            key: "Key: G",
            
            musicStyle: "",
            
            translation: "C.W. 169, S.H. 250",
            
            verse1: "Itna Kumpi, ka tuu-cing pa, \nAma hoih na sia ngei lo, \nA mi suak leng sapna om lo, \nTawntung Ama mi hi ta.",
            
            verse2: "Nuntakna tui a luanna ah, \nTat khiatsa ka kha tonpih, \nLo no naah hong paipih hi, \nVantung an tawh hong vaak hi.",
            
            verse3: "Khuamial sihna kuam sung lau lo, \nKei kiang Topa Na om hi, \nKa lung nuam sak, Na ciangkhut in, \nLam hong hilh singlamteh in.",
            
            verse4: "Ni sim tawntung kizom sual in, \nNa hoihna kipelh ngei lo, \nTuucing siampa, Nang kong phat hi, \nTawntung nangma innpi sung Amen.",
            
            isFavoriteSong: false,
            isNotedSong: false,
            isHighlightedSong: false,
            isBookMarkedSong: false,
            isSharedSong: false,
            isCopySong: false),
            
        Lyric(number: 3,
              
            zoTitle: "Kumpipa Bia Un",
            
            engTitle: "O Worship the King",
            
            key: "Key: G",
            
            musicStyle: "",
            
            translation: "S.H. 4",
            
            verse1: "Vantung zahtak huai Kumpipa bia un. \nA vaanglian leh hong itna la sa un; \nA hong liah hong keem hong uk tawntung maangpi \nA vaan-lian hi, ci in phat huai Topa hi.",
            
            verse2: "Hongit hong hehpihna van pan taang-a \nA vaang liat-na thu la in sa unla \nA heh ciangin kek-kia tawh kibang ding hi. \nA thu khen-na in haksa mahmah ding hi.",
            
            verse3: "Na lamdang tawh a dim leitung in-ah \nVaanglian tawntung tawh Pasian a hong piang. \nPel ngei lo na hanciamna tawh a kip hi. \nPuan bangin a kiim-kot tuipi hi sak hi.",
            
            verse4: "Naupang bangin thanem leivui tate, \nNang sung ah muan'na bukna kong mu diam; \nNong hehpihna kicing a tawpdong inah, \nHong bawl pa, hong dal pa, hong gum pa i Lawm.",
            
            isFavoriteSong: false,
            isNotedSong: false,
            isHighlightedSong: false,
            isBookMarkedSong: false,
            isSharedSong: false,
            isCopySong: false),
            
        Lyric(number: 4,
          
            zoTitle: "Pa Tung Min Than'na Om Hen",
            
            engTitle: "Gloria Patri (1st Tune)",
            
            key: "Key: A",
            
            musicStyle: "",
            
            translation: "C.W. 614",
            
            verse1: "Pa tung min than'na om hen, \nTapa tung leh Kha Siangtho tung ah zong, \nPiancil lai leh tu-hun bang-in, \nA tawntung in hih leitung om den ding hi. \nAmen Amen.",
            
            isFavoriteSong: false,
            isNotedSong: false,
            isHighlightedSong: false,
            isBookMarkedSong: false,
            isSharedSong: false,
            isCopySong: false),
            
        Lyric(number: 5,
        
            zoTitle: "Pa Tung Min Than'na Om Hen",
            
            engTitle: "Gloria Patri (2nd Tune)",
            
            key: "Key: F",
            
            musicStyle: "",
            
            translation: "C.W. 613",
            
            verse1: "Pa tung min than'na om hen, \nTapa tung leh Kha Siangtho tung ah zong, \nPiancil lai leh tu-hun bang-in, \nA tawntung in hih leitung om den ding hi. \nAmen Amen.",
            
            isFavoriteSong: false,
            isNotedSong: false,
            isHighlightedSong: false,
            isBookMarkedSong: false,
            isSharedSong: false,
            isCopySong: false)
    ]
    
}


import Foundation
import SwiftUI

class TapToggle: ObservableObject {
    
    @Published var songLyrics: [Lyric] = LyricList.hymnLa
    
    @Published var shouldShowFavorites: Bool = false
    
    // Hymn
    @Published var isHymnSearchTappped: Bool = false
    @Published var isFavorite: Bool = false
    
}


struct Hymn: View {
    
    @AppStorage("darkModeOn") private var darkModeOn: Bool = false
    
    var body: some View {
        VStack {
            ZStack {
               HymnLyrics()
               HymnTabView()
                }
            }
        }
    }
}



struct Hymn_Previews: PreviewProvider {
    static var previews: some View {
        Hymn().environmentObject(TapToggle())
            
    }
}

struct HymnTabView: View {
    
    @EnvironmentObject var tappingSwitches: TapToggle
    
    var body: some View {
        
        VStack(spacing: 0) {
            
            Spacer()
            
            HStack {
                
                Spacer()
                
                Group {
                    
                    Spacer()
                    Button(action: {
                        
                        withAnimation {
                            self.tappingSwitches.shouldShowFavorites = true
                        }
                        
                    }, label: {
                        Image(systemName: "suit.heart")
                            .font(.system(size: 24, weight: .bold))
                    })
                    .fullScreenCover(isPresented: $tappingSwitches.shouldShowFavorites, content: {
    
                        HymnFavoriteView(isPresented: $tappingSwitches.shouldShowFavorites)
                    })
                    
                    
                    Spacer()

                }
                
                Spacer()
                
            }
            .foregroundColor(Color("cTextColor"))
            .padding(.vertical, 5.0)
            .background(Color("cTabColor"))
            .clipShape(Capsule())
            .padding(.horizontal)
            
        }
        
    }
}

struct HymnLyrics: View {
    
    @EnvironmentObject var tap: TapToggle
    
    var body: some View {
        
        NavigationView {
            
            ScrollView (.vertical, showsIndicators: false) {
                LazyVStack(spacing: 5) { 

                    ForEach (tap.songLyrics, id: \.id) { lyric in

                        MainLyricView(lyric: lyric)
                        
                        
                        Divider()
                            .padding(.all)
                    }
                }
            }
            .navigationBarHidden(true)
        }
        .navigationViewStyle(StackNavigationViewStyle())
    }
}

struct MainLyricView: View {

    @State var lyric: Lyric
    @EnvironmentObject var tap: TapToggle
    @State var selectedFavoriteSong: Bool = false
    
    
    var body: some View {
        Group {
            Group {
                HStack {
                    Button(action: {
                        self.tap.isFavorite.toggle()
                    }, label: {
                        if tap.isFavorite {
                            Image(systemName: "suit.heart.fill")
                                .foregroundColor(.red)
                                .padding(.horizontal)

                        } else {
                            Image(systemName: "suit.heart")
                                .padding(.horizontal)
                        }
                    })
                    
                    Spacer()
                    Text("\(lyric.number)")

                    Spacer()
                    
                }
                .padding(.top)
                
                VStack {
                    
                    Text(lyric.zoTitle)
                        .font(.title2.smallCaps())
                        .fontWeight(.bold)
                        .foregroundColor(.primary)
                    
                    Text(lyric.engTitle/*.capitalized*/)
                        .font(.title3)
                        .fontWeight(.medium)
                        .foregroundColor(.secondary)
                        .italic()
                }
                .padding(.horizontal)
                .multilineTextAlignment(.center)
                
                
                HStack {
                    Text(lyric.key)
                        .italic()
                    
                    Spacer()
                    
                    Text(lyric.musicStyle)
                }
                .foregroundColor(.blue)
                .padding(.all)
            }

        }
    }
}

struct TitleDetailView: View {
    
    @State var lyric: Lyric
    @EnvironmentObject var tap: TapToggle
    
    var body: some View {
        Group {
            Group {
                HStack {
                    Button(action: {
                        self.tap.isFavorite.toggle()
                    }, label: {
                        if tap.isFavorite {
                            Image(systemName: "suit.heart.fill")
                                .foregroundColor(.red)
                                .padding(.horizontal)

                        } else {
                            Image(systemName: "suit.heart")
                                .padding(.horizontal)
                        }
                    })
                    
                    Spacer()
                    Text("\(lyric.number)")
                    Spacer()
                    
                    
                    
                }
                .padding(.top)
                
                VStack {
                    
                    Text(lyric.zoTitle)
                        .font(.title2.smallCaps())
                        .fontWeight(.bold)
                        .foregroundColor(.primary)
                    
                    Text(lyric.engTitle/*.capitalized*/)
                        .font(.title3)
                        .fontWeight(.medium)
                        .foregroundColor(.secondary)
                        .italic()
                }
                .padding(.horizontal)
                .multilineTextAlignment(.center)
                
                
                HStack {
                    Text(lyric.key)
                        .italic()
                    
                    Spacer()
                    
                    Text(lyric.musicStyle)
                }
                .foregroundColor(.blue)
                .padding(.all)
            }
        }

        Spacer()
        
    }
}

struct HymnSearch: View {
    
    @Binding var isPresented: Bool
    
    @State var searchResults: [Lyric] = LyricList.hymnLa
    @State private var searchLyrics = ""
    @State private var isSearchDelete = false
    
    var body: some View {
        NavigationView {
            ZStack {
                Color("bColor")
                    .edgesIgnoringSafeArea(.all)
                    //.shadow(radius: 20)
                VStack (alignment: .center, spacing: 10) {
                    
                    HStack {
                        HSearchHome(searchLyrics: $searchLyrics, isSearchDelete: $isSearchDelete)
                        Spacer()
                        Button(action: {

                            isPresented = false

                        }, label: {
                            Image(systemName: "music.note.house")
                                .imageScale(.large)
                                .padding(4.0)
                        })
                    }
                    .padding(.top, 5.0)
                    .padding(.horizontal)
                    
                    ScrollView (.vertical, showsIndicators: false) {
                                LazyVStack(alignment: .leading, spacing: 10) {
                                    
                                    ForEach (searchResults.filter({"\($0)".range(of: searchLyrics, options: [.numeric, .caseInsensitive]) != nil

                                    }), id: \.id) { searchSongs in
                                        
                                        NavigationLink(
                                            destination: TitleDetailView(lyric: searchSongs),
                                            label: {
                                                SearchDetailView(searchSongs: searchSongs, searchLyrics: $searchLyrics)
                                            })

                                        } // The end of ForEach
                                    
                                    }.padding(.horizontal)
                                }

                }
                .navigationBarHidden(true)
            }
        }
    }
}


struct HSearchHome: View {
    
    @Binding var searchLyrics: String
    @Binding var isSearchDelete: Bool
    
    var body: some View {
        
        HStack {
            
            Image (systemName: "magnifyingglass")
                .foregroundColor(.blue)
                .padding(.leading)
            
            TextField("Search ...", text: $searchLyrics)
                .autocapitalization()
                .disableAutocorrection(true)
                .onTapGesture {
                    self.isSearchDelete = true
                }
            
            if isSearchDelete {
                Image (systemName: "xmark.circle.fill")
                    .foregroundColor(.secondary)
                    .padding([.top, .bottom, .trailing])
                    //.background(Color.red)
                    .onTapGesture {
                        withAnimation {
                            searchLyrics = ""
                            self.isSearchDelete = false
                            UIApplication.shared.dismissKeyboard()
                        }
                    }
            }
        }
        .foregroundColor(.primary)
        .font(.title3)
        .frame(width: 350, height: 40)
        .background(Color("cTabColor"))
        .cornerRadius(20)
    }
}


extension UIApplication {
    func dismissKeyboard() {
        sendAction(#selector (UIResponder
                    .resignFirstResponder), to: nil, from: nil, for: nil)
    }
}

struct SearchDetailView: View {
    
    var searchSongs: Lyric
    @Binding var searchLyrics: String
    @EnvironmentObject var tap: TapToggle
    
    var body: some View {
        ZStack {
            Color("cTabColor")
            VStack(alignment: .leading, spacing: 0) { // With Lazy, divider not work. Test without Lazy for multiple lyrics
                
                HStack {
                    Text("\(searchSongs.number)")
                        .font(.title2)
                        .foregroundColor(.primary)
                        .padding()
                    
                    VStack(alignment: .leading, spacing: 0) {
                        Text(searchSongs.zoTitle)
                            .foregroundColor(.primary)
                            .fontWeight(.bold)
                        
                        Text(searchSongs.engTitle)
                            .fontWeight(.medium)
                            .foregroundColor(.secondary)
                            .italic()
                        
                    }
                    .lineLimit(1)
                    .minimumScaleFactor(0.5)
                }
                
                Divider()
                    .padding(.horizontal)
            }
            
        }
        .cornerRadius(20)
    }
}

标签: swiftui

解决方案


推荐阅读