Considering the below code, I would expect my TabView to show the randomly calculated image when my tab view is displayed, however this is not the case. It will always display the first image at index 0. If I hardcode the selection in the TabView initialiser (i.e. .constant(n)
) then it will display the chosen image correctly. I have filed a radar (FB7844985) on this, but want to check that I am not missing something obvious?
struct ContentView: View {
let assets: [String] = (1...32).map { "preview_\($0)"}
@State private var selection: Int = 0
@State private var isPresented: Bool = false
var body: some View {
VStack {
Button("Random element") {
selection = Int.random(in: 1...32)
isPresented = true
}
}
.sheet(isPresented: $isPresented) {
SlideshowView(selection: $selection, assets: assets)
}
}
}
struct SlideshowView: View {
@Binding var selection: Int
var assets: [String]
var body: some View {
TabView(selection: $selection) {
ForEach(assets.indices, id: \.self) { index in
Image(assets[index])
.resizable()
.aspectRatio(contentMode: .fit)
}
}
.tabViewStyle(PageTabViewStyle())
.indexViewStyle(PageIndexViewStyle(backgroundDisplayMode: .always))
}
}
The reason is not in modality (ie showing in sheet), but in that TabView
does not read initial (!) selection (and this is definitely a SwiftUI bug)
Here is workaround (tested with Xcode12 / iOS 14 on replicated code)
struct SlideshowView: View {
@Binding var selection: Int
var assets: [String]
var body: some View {
TabView(selection: $selection) {
ForEach(assets.indices, id: \.self) { index in
Image(assets[index])
.resizable()
.aspectRatio(contentMode: .fit)
}
}
.tabViewStyle(PageTabViewStyle())
.indexViewStyle(PageIndexViewStyle(backgroundDisplayMode: .always))
.onAppear {
// WORKAROUND: simulate change of selection on appear !!
let value = selection
selection = -1
DispatchQueue.main.async {
selection = value
}
}
}
}