My app is movie app. There is a SingleMovieView
to present one movie's related informations, like
Director, Cast... And in the bottom, it is a scroll view to show the similar movies list. When user click one item of it, then should navigate to the selected movie view.
Well, the problem is actually it is same SingleMovieView
for the destination. Or say that I want to reuse this view with different data for the selected movie. Currently, I'm using navigation link in ForEach of RecMovieList
, and the taping is not working. So, how to do the self-invoke for the same View in SwiftUI?
Here is the code:
import SwiftUI
import KingfisherSwiftUI
struct SingleMovieView: View {
var movieId: Int = -1
@ObservedObject var model = MovieListViewModel()
var body: some View {
ScrollView(showsIndicators: false) {
VStack(alignment: .leading) {
createPosterImage()
MovieDetailView(movie: self.model.movie)
if model.secSectionMoviesBundle.isEmpty {
Text("Loading")
} else {
VStack(alignment: .leading, spacing: 12) {
CrewList(crews: (model.secSectionMoviesBundle[.Crew] as! [CrewViewModel]).filter {$0.job == "Director"} )
CastList(casts: model.secSectionMoviesBundle[.Cast] as! [CastViewModel])
ImageList(images: model.secSectionMoviesBundle[.Images] as! [ImageViewModel])
RecMovieList(movies: model.secSectionMoviesBundle[.Recomm] as! [MovieViewModel])
}
}
}
}.edgesIgnoringSafeArea(.top)
.onAppear() {
self.model.getMovieDetail(id: self.movieId)
self.model.getSecSectionMoviesBundle(id: self.movieId)
}
}
// struct CrewList
// struct CastList
// struct ImageList
struct RecMovieList: View {
var movies: [MovieViewModel]
var body: some View {
VStack(alignment: .leading) {
Text("\(SecHomeSection.Recomm.rawValue)")
.font(.headline)
ScrollView(.horizontal) {
HStack(alignment: .top, spacing: 10) {
ForEach(0..<movies.count) { i in
VStack(alignment: .leading) {
NavigationLink(destination: SingleMovieView(movieId: self.movies[i].id)) {
KFImage(source: .network(self.movies[i].posterUrl))
.renderingMode(.original)
.resizable()
.frame(width: 100, height: 150)
.aspectRatio(2/3, contentMode: .fill)
}
Text("\(self.movies[i].title)")
.lineLimit(2)
.foregroundColor(.gray)
.frame(height: 50, alignment: .top)
}.frame(width: 100)
}
}
}
//.frame(height: 100)
}
.padding(.horizontal).padding(.bottom)
}
}
fileprivate func createPosterImage() -> some View {
return KFImage(source: .network(model.movie.posterUrl))
.resizable().aspectRatio(contentMode: .fit)
}
}
// Update:, as the comment, I realize this is the reloading data of current view instead of navigation. So I add the ObservedObject in struct RecMovieList too, then now the published model could be used in this struct too, to redo the networking request in onAppear().
And I change NavigationLink to Button as there is no navigation needed. Well, it works and the view could reload, but when I tap the movie list in the app, it triggered the touch event/reloading data, even for the dragging, swiping. How could I fix this thing? Use TapGesture?
struct RecMovieList: View {
var movies: [MovieViewModel]
@ObservedObject var model = MovieListViewModel()
var body: some View {
VStack(alignment: .leading) {
Text("\(SecHomeSection.Recomm.rawValue)")
.font(.headline)
ScrollView(.horizontal) {
HStack(alignment: .top, spacing: 10) {
ForEach(0..<movies.count) { i in
VStack(alignment: .leading) {
Button(action: {
self.model.getMovieDetail(id: self.movies[i].id)
self.model.getSecSectionMoviesBundle(id: self.movies[i].id)
}) {
KFImage(source: .network(self.movies[i].posterUrl))
.renderingMode(.original)
.resizable()
.frame(width: 100, height: 150)
.aspectRatio(2/3, contentMode: .fill)
}
Text("\(self.movies[i].title)")
.lineLimit(2)
.foregroundColor(.gray)
.frame(height: 50, alignment: .top)
}.frame(width: 100)
}
}
}
//.frame(height: 100)
}
.padding(.horizontal).padding(.bottom)
}
}