I am trying to open a sheet when tapping on an item. I followed this questions Sheet inside ForEach doesn't loop over items SwiftUI answer. I get this Error: The compiler is unable to type-check this expression in reasonable time; try breaking up the expression into distinct sub-expressions
, I don't understand what is causing it. I tried multiple solutions and they all lead to the same Error.
@State var selectedSong: Int? = nil
@State var songList: [AlbumSong] = []
VStack {
ForEach(songList.enumerated().reversed(), id: \.offset) { index, song in
HStack {
Text("\(index 1).").padding(.leading, 8)
VStack {
Text(song.title)
Text(song.artist)
}
}.onTapGesture {
self.selectedSong = index
}
}
}
}
.sheet(item: self.$selectedSong) { selectedMovie in
SongPickerEdit(songList: $songList, songIndex: selectedMovie)
I also tried setting songIndex
to being an AlbumSong
and then implemented this sheet:
.sheet(item: self.$selectedSong) {
SongPickerEdit(songList: $songList, songIndex: self.songList[$0])
}
struct SongPickerEdit: View {
@Binding var songList: [AlbumSong]
@State var songIndex: Int?
var body: some View {
}
}
struct AlbumSong: Identifiable, Codable {
@DocumentID var id: String?
let title: String
let duration: TimeInterval
var image: String
let artist: String
let track: String
}
CodePudding user response:
How about making selectedSong
an AlbumSong?
? The item:
parameter needs to be an Identifiable
binding, but Int
is not Identifiable
.
@State var selectedSong: AlbumSong? = nil
@State var songList: [AlbumSong] = []
var body: some View {
List {
ForEach(songList.enumerated().reversed(), id: \.offset) { index, song in
HStack {
Text("\(index 1).").padding(.leading, 8)
VStack {
Text(song.title)
Text(song.artist)
}
}.onTapGesture {
self.selectedSong = song
}
}
}.sheet(item: $selectedSong) { song in
SongPickerEdit(songList: $songList, song: song)
}
}
Note that SongPickerEdit
would look like this:
struct SongPickerEdit: View {
@State var song: AlbumSong
var body: some View {
Text("\(song.title), \(song.artist)")
}
}
If you really need the index for some reason, you can add the song list binding back in and use songList.index { $0.id == song.id }
to find the index if the list is not too long.
Otherwise, you can make your own Identifiable
type SongAndIndex
that uses the same id
as AlbumSong
, but with an extra index property, and use that as the type of selectedSong
.
A third way would be to use the sheet(isPresented:)
overload, but this way you end up with 2 sources of truth:
@State var selectedSongIndex: Int? = nil {
didSet {
if selectedSongIndex != nil {
isSheetPresented = true
}
}
}
@State var isSheetPresented: Bool = false {
didSet {
if !isSheetPresented {
selectedSongIndex = nil
}
}
}
...
}.sheet(isPresented: $isSheetPresented) {
SongPickerEdit(songList: $songList, songIndex: selectedSongIndex)
}
selectedSongIndex
also won't be set to nil when the user dismisses the sheet.