supporting iOS 14, my problem is that THE FIRST TIME I tap on a cell, even if movie is valorized, no info is shown, after that, everything is working properly. Consider that when tapped on button, I print correctly info even first time. My view is in a tab but I don't think it is a problem. urls are not valorized since I'm testing what happens if my view is failing in downloading image.
import SwiftUI
struct Movie: Identifiable {
let id = UUID().uuidString
var title: String?
var year: Int?
var url: String?
}
//iniztialized inside entry point of the app
class ClassFromEntryPoint: ObservableObject {
@Published var fromEntryPointValue = ""
@Published var movies: [Movie] = []
init() {
movies.append(Movie(title: "Back to the future", year: 1985))
movies.append(Movie(title: "Jaws", year: 1975))
movies.append(Movie(title: "Start Wars", year: 1979))
}
}
import SwiftUI
import Combine
class ImageLoader: ObservableObject {
@Published var image: UIImage?
private var cancellables = Set<AnyCancellable>()
func load(fromURLString urlString: String) {
guard let url = URL(string: urlString) else {
return
}
URLSession.shared.dataTaskPublisher(for: url)
.map { UIImage(data: $0.data) }
.replaceError(with: nil)
.receive(on: DispatchQueue.main)
.sink { [weak self] in self?.image = $0 }
.store(in: &cancellables)
}
}
struct AsyncImageView: View {
@StateObject private var loader = ImageLoader()
let placeholderImage = Image(systemName: "photo")
var url: String
var body: some View {
Group {
if let image = loader.image {
Image(uiImage: image)
.resizable()
} else {
placeholderImage
.resizable()
}
}
.onAppear {
loader.load(fromURLString: url)
}
}
}
struct FourthViewWithGrid: View {
// Created inside an entry point of the app
@EnvironmentObject var classFromEntryPoint: ClassFromEntryPoint
@State private var movies: [Movie] = []
@State private var selectedMovie: Movie?
@State private var isShowingDetails = false
let columns = [
GridItem(.flexible()),
GridItem(.flexible())
]
var body: some View {
NavigationView {
ScrollView {
LazyVGrid(columns: columns, spacing: 20) {
ForEach(movies) { movie in
Button(action: {
self.selectedMovie = movie
var _ = print("selected: \(movie)")
DispatchQueue.main.async {
self.isShowingDetails = true
}
}) {
VStack {
AsyncImageView(url: movie.url ?? "")
.aspectRatio(contentMode: .fill)
.frame(width: 100, height: 100)
.cornerRadius(10)
Text(movie.title ?? "ND/")
.font(.headline)
.lineLimit(1)
if let year = movie.year {
Text("\(year)")
.font(.subheadline)
}
}
}
}
}
.padding()
}
.navigationTitle("Movies")
.sheet(isPresented: $isShowingDetails) {
// show details
if let selectedMovie = selectedMovie {
Text(selectedMovie.title ?? "default title")
} else {
var _ = print("no selected movie")
EmptyView()
}
}
}
.onAppear {
movies = classFromEntryPoint.movies
}
}
}