I have a tabview (page style) that I am using to create an automatic slideshow. Each slide is not assigned to the currentIndex for some odd reason. Neither does the timer work or manually switching tabs with the default dot controls given with tabview.
If i set the tag to selectedIndex the tabview will default to the first slide. No errors to the console. Any help is greatly appreciated!
struct HomeView: View {
@EnvironmentObject private var hvm: HomeViewModel
@State private var selectedFilter: String = "Popular"
@State private var selectedMedia: MediaModelResult? = nil
@State private var showDetailView: Bool = false
@State private var currentIndex: Int = 0
@State var isFilterSelected: Bool = true
@Namespace var animation
private let timer = Timer.publish(every: 3, on: .main, in: .common).autoconnect()
var body: some View {
NavigationView {
ZStack {
Color.theme.background.ignoresSafeArea()
VStack {
header
VStack(alignment: .leading, spacing: 15) {
Text("Upcoming Movies")
.foregroundColor(Color.theme.secondaryAccent)
.font(.subheadline)
.padding(.leading, 15)
TabView(selection: $currentIndex) {
ForEach(hvm.upcomingFilms) { film in
MediaImageView(mediaPath: film, poster: false)
.scaledToFill()
.tag(film)
}
}
.tabViewStyle(PageTabViewStyle())
.clipShape(RoundedRectangle(cornerRadius: 5))
.padding(.horizontal, 15)
.frame(width: UIScreen.main.bounds.width, height: 180)
.onReceive(timer, perform: { _ in
withAnimation(.default) {
currentIndex = currentIndex < hvm.upcomingFilms.count ? currentIndex + 1 : 0
}
})
}
scrollViewContent
}
}
.preferredColorScheme(.dark)
.navigationBarHidden(true)
}
}
}
struct MediaImageView: View {
@StateObject var mivm: MediaImageViewModel
var poster: Bool
init(mediaPath: MediaModelResult, poster: Bool) {
self.poster = poster
_mivm = StateObject(wrappedValue: MediaImageViewModel(mediaPath: mediaPath))
}
var body: some View {
if poster {
ZStack {
if let image = mivm.poster {
Image(uiImage: image)
.resizable()
.scaledToFit()
} else if mivm.isLoading {
ProgressView()
}
}
} else {
ZStack {
if let image = mivm.backdrop {
Image(uiImage: image)
.resizable()
} else if mivm.isLoading {
ProgressView()
}
}
}
}
}
class HomeViewModel: ObservableObject {
@Published var tabBarImageNames = ["house", "rectangle.stack", "clock.arrow.circlepath", "magnifyingglass"]
@Published var filterTitles = ["Popular Now", "Top Rated", "New"]
@Published var popularFilms: [MediaModelResult] = []
@Published var topRatedFilms: [MediaModelResult] = []
@Published var upcomingFilms: [MediaModelResult] = []
@Published var popularTV: [MediaModelResult] = []
@Published var topRatedTV: [MediaModelResult] = []
private let dataService = MediaDataService()
private var cancellables = Set<AnyCancellable>()
init() {
addSubscribers()
}
func addSubscribers() {
dataService.$popularFilms
.sink { [weak self] (returnedFilms) in
self?.popularFilms = returnedFilms
}
.store(in: &cancellables)
dataService.$topRatedFilms
.sink { [weak self] (returnedFilms) in
self?.topRatedFilms = returnedFilms
}
.store(in: &cancellables)
dataService.$upcomingFilms
.sink { [weak self] (returnedFilms) in
self?.upcomingFilms = returnedFilms
}
.store(in: &cancellables)
dataService.$popularTV
.sink { [weak self] (returnedFilms) in
self?.popularTV = returnedFilms
}
.store(in: &cancellables)
dataService.$topRatedTV
.sink { [weak self] (returnedFilms) in
self?.topRatedTV = returnedFilms
}
.store(in: &cancellables)
}
}
Solution:
TabView(selection: $currentIndex) {
ForEach(0..<hvm.upcomingFilms.count) { film in
MediaImageView(mediaPath: hvm.upcomingFilms[film], poster: false)
.scaledToFill()
}
}