将内容模型转换为 EnvironmentObject [英] Convert Contentful Model to EnvironmentObject
问题描述
我需要将我的 Contentful
模型转换为可在多个视图之间访问.
I need to convert my Contentful
model to be accessible between multiple views.
因此,例如,我将有一个电影列表和每个将有一个图像,标题和;预告片的 URL.我将有一个处理图像的视图,当用户点击图像时,我希望标题的视图更新 &将根据选择播放要更新的预告片的视图.
So, for example, I will have a list of movies & each will have an image, title & a URL to a trailer. I will have a view for handling the images and when a user taps on an image I would like the view for the title to update & the view that will play the trailer to update based on the selection.
下面更新的最佳方式是什么,以便当我调用 selectableRow
时,我可以更新我的标题 &我的视频播放器有正确的数据?
What would be the best way to update below so that when I call selectableRow
I can update my title & my video player with the correct data?
我已经尝试将我的 Movie
结构更新为一个类并使其成为 ObservableObject
但这只会导致错误.
I've tried updating my Movie
struct to a class and making it ObservableObject
but that just leads to errors.
import SwiftUI
import Combine
import Contentful
struct Movie: Codable, Identifiable, FieldKeysQueryable, EntryDecodable {
static let contentTypeId: String = "movies"
// FlatResource Memberes.
let id: String
var updatedAt: Date?
var createdAt: Date?
var localeCode: String?
var title: String
var movieId: String
var movieTrailer: String
enum FieldKeys: String, CodingKey {
case title, movieId, movieTrailer
}
enum CodingKeys: String, CodingKey {
case id = "id"
case title = "title"
case movieId = "movieId"
case movieTrailer = "movieTrailer"
}
}
public class MovieFetcher: ObservableObject {
@Published var movies = [Movie]()
init() {
getArray(id: "movies") { (items) in
items.forEach { (item) in
self.movies.append(Movie(id: item.id, title: item.title, movieId: item.movieId, movieTrailer: item.movieTrailer))
}
}
}
func getArray(id: String, completion: @escaping([Movie]) -> ()) {
let client = Client(spaceId: spaceId, accessToken: accessToken, contentTypeClasses: [Movie.self])
let query = QueryOn<Movie>.where(contentTypeId: "movies")
client.fetchArray(of: Movie.self, matching: query) { (result: Result<ArrayResponse<Movie>>) in
switch result {
case .success(let array):
DispatchQueue.main.async {
completion(array.items)
}
case .error(let error):
print(error)
}
}
}
}
struct moviesView : View {
@ObservedObject var fetcher = MovieFetcher()
@State var selectMovie: Movie? = nil
@Binding var show: Bool
var body: some View {
HStack(alignment: .bottom) {
if show {
ScrollView(.horizontal) {
Spacer()
HStack(alignment: .bottom, spacing: 30) {
ForEach(fetcher.movies, id: \.self) { item in
selectableRow(movie: item, selectMovie: self.$selectMovie)
}
}
.frame(minWidth: 0, maxWidth: .infinity)
}
.padding(.leading, 46)
.padding(.bottom, 26)
}
}
}
}
struct selectableRow : View {
var movie: Movie
@Binding var selectedMovie: Movie?
@State var initialImage = UIImage()
var body: some View {
ZStack(alignment: .center) {
if movie == selectedMovie {
Image("")
.resizable()
.frame(width: 187, height: 254)
.overlay(
RoundedRectangle(cornerRadius: 13)
Image(uiImage: initialImage)
.resizable()
.cornerRadius(13.0)
.frame(width: 182, height: 249)
.onAppear {
let urlString = "\(urlBase)\(self.movie.movieId).png?"
guard let url = URL(string: self.urlString) else { return }
URLSession.shared.dataTask(with: url) { (data, response, error) in
guard let data = data else { return }
guard let image = UIImage(data: data) else { return }
RunLoop.main.perform {
self.initialImage = image
}
}.resume()
}
} else {
Image(uiImage: initialImage)
.resizable()
.cornerRadius(13.0)
.frame(width: 135, height: 179)
.onAppear {
let urlString = "\(urlBase)\(self.movie.movieId).png?"
guard let url = URL(string: self.urlString) else { return }
URLSession.shared.dataTask(with: url) { (data, response, error) in
guard let data = data else { return }
guard let image = UIImage(data: data) else { return }
RunLoop.main.perform {
self.initialImage = image
}
}.resume()
}
}
}
.onTapGesture {
self.selectedMovie = self.movie
}
}
}
struct mainView : View {
@ObservedObject var fetcher = MovieFetcher()
@State private var show = true
var body: some View {
ZStack {
trailerView(show: $show)
titleView(show: $show)
moviesView(show: $show)
}
}
}
class hostingController: UIHostingController<mainView> {
required init?(coder: NSCoder) {
super.init(coder: coder,rootView: mainView().environmentObject(Movie())); // Getting error here
}
override func viewDidLoad() {
super.viewDidLoad()
}
}
推荐答案
我假设您想在 UIKit 项目中使用 mainView.
I'm assuming you want to use the mainView in a UIKit project.
不确定您要做什么,但您不能这样做:
Not sure what you are trying to do, but you can't do this:
required init?(coder: NSCoder) {
super.init(coder: coder,rootView: mainView().environmentObject(Movie())); // Getting error here
}
由于您已经有了一个可观察对象 (MovieFetcher),您可以在 mainView 或任何其他视图中使用 fetcher.movies 访问电影.
Since you already have an observable object (MovieFetcher), you can access the movies using fetcher.movies in the mainView or any other view.
这篇关于将内容模型转换为 EnvironmentObject的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!