如何在切换 SwiftUI 中使用 json 属性?

Posted

技术标签:

【中文标题】如何在切换 SwiftUI 中使用 json 属性?【英文标题】:How to use json property in a toggle SwiftUI? 【发布时间】:2020-06-06 11:31:51 【问题描述】:

我尝试动态编程切换以在我的 SwiftUI 项目中显示较短的列表。

我使用的 JSON 文件是here。 我的项目在GitHub。 我像这样对我的切换进行编程,但是当我打开它时,它没有显示正确的列表。目标是将 JSON isShow 属性链接到 toggle 属性。当toggle关闭时,会显示完整列表,当toggle打开时,只会显示isShow=FALSE列表。

ForEach(0..<self.userData.movies.filter self.showShowOnly == true ?  $0.isShow == self.showShowOnly  : true .count, id: \.self)  movieIndex in
                        VStack (alignment: .leading) 
                            NavigationLink(destination: ContentView(movie: self.$userData.movies[movieIndex])) 
                                Text(self.userData.movies[movieIndex].nom)
                            

                        

电影结构是

struct Movie: Decodable, Encodable, Identifiable 
public var id: Int
public var nom: String
public var idL: String
public var note: Int
public var isShow: Bool

enum CodingKeys: String, CodingKey 
       case id = "id"
       case nom = "nom"
       case idL = "idL"
       case note = "note"
       case isShow = "isShow"
    

json 提取器是:

public class MovieFetcher: ObservableObject 
func save(movies: [Movie]) 

    OperationQueue().addOperation 

        do 
            let encoded = try JSONEncoder().encode(movies)
            let url = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)

            do 
                try encoded.write(to: url[0].appendingPathComponent("movies"))
                print("saved to ", url[0])
             catch 
                print(error)
            

         catch 
            print(error)
        
    


func load(completion: @escaping (([Movie]?) -> () )) 

    let urls = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)

    do 
        if let data = try? Data(contentsOf: urls[0].appendingPathComponent("movies")) 

            let decodedLists = try JSONDecoder().decode([Movie].self, from: data as Data)
            DispatchQueue.main.async 
                completion(decodedLists)
            


         else 
            loadFromJson(completion: completion)
        
     catch 
        print(error)
        completion(nil)
    


func loadFromJson(completion: @escaping (([Movie]?) -> () )) 

    let url = URL(string: "https://gist.githubusercontent.com/thomjlg/0782e9e8e27c346af3600bff9923f294/raw/9705fb0c6b40eae59578755b86e331bea257972b/films2.json")!

    URLSession.shared.dataTask(with: url) (data,response,error) in
        do 
            if let d = data 
                let decodedLists = try JSONDecoder().decode([Movie].self, from: d)
                DispatchQueue.main.async 
                    completion(decodedLists)
                
            else 
                print("No Data")
                completion(nil)
            
         catch 
            print ("Error")
            completion(nil)
        

    .resume()


有人知道如何更正我的代码吗?谢谢

【问题讨论】:

【参考方案1】:

过滤器代码有效。请注意,在 323 部电影中,只有 1 部电影“isShow”=true。

  "nom":"Ratatouille",
        "id":217,
        "idL":"R",
        "note":0,
        "isShow":true

问题是在 ForEach 循环中使用“movieIndex”的方式。 “movieIndex”是指过滤后的 self.userData.movi​​es 列表的索引。这个“电影索引” 不应在 self.userData.movi​​es[movieIndex] 中使用。

解决这个问题:

                    ForEach(self.userData.movies.filter self.showShowOnly ? $0.isShow == !self.showShowOnly : true )  movie in
                        VStack (alignment: .leading) 
                            NavigationLink(destination: ContentView(movie: movie)) 
                                Text(movie.nom)
                            
                        
                     

试试这个,让我知道它是否有效。

编辑:试试这个类来代替结构体

open class Movie: Codable, Identifiable, Equatable, Hashable, ObservableObject 

@Published public var id: Int
@Published public var nom: String
@Published public var idL: String
@Published public var note: Int
@Published public var isShow: Bool

enum CodingKeys: String, CodingKey 
    case id = "id"
    case nom = "nom"
    case idL = "idL"
    case note = "note"
    case isShow = "isShow"


public static func == (lhs: Movie, rhs: Movie) -> Bool 
    lhs.id == rhs.id


public func hash(into hasher: inout Hasher) 
    hasher.combine(id)


public init(id: Int, nom: String, idL: String, note: Int, isShow: Bool) 
    self.id = id
    self.nom = nom
    self.idL = idL
    self.note = note
    self.isShow = isShow


public init() 
    self.id = UUID().hashValue
    self.nom = ""
    self.idL = ""
    self.note = 0
    self.isShow = false


public required init(from decoder: Decoder) throws 
    let container = try decoder.container(keyedBy: CodingKeys.self)
    self.id = try container.decode(Int.self, forKey: .id)
    self.nom = try container.decode(String.self, forKey: .nom)
    self.idL = try container.decode(String.self, forKey: .idL)
    self.note = try container.decode(Int.self, forKey: .note)
    self.isShow = try container.decode(Bool.self, forKey: .isShow)


public func encode(to encoder: Encoder) throws 
    var container = encoder.container(keyedBy: CodingKeys.self)
    try container.encode(id, forKey: .id)
    try container.encode(nom, forKey: .nom)
    try container.encode(idL, forKey: .idL)
    try container.encode(note, forKey: .note)
    try container.encode(isShow, forKey: .isShow)


请注意,您必须在 ContentView 中将“@Binding var movie : Movie”更改为“@ObservedObject var movie: Movie”并调整“ContentView_Previews”。

【讨论】:

请注意,Movie 是一个结构,您不能像在 ContentView 中那样更改它的值,而是使用一个类,或者更好的是使用 ObservableObject 类。 感谢您的回答。我看不到如何调整ContentView_Previews ...我尝试了menu(movie: movie),但它不起作用。我还必须更改 SceneDelegate.swift 文件中的menu() 在 ContentView_Previews 中只做:“ContentView(movie: Movie(id: 1, nom: "A", idL: "B", note: 2, isShow: true), note: 3)"您不必更改 SceneDelegate.swift 中的 menu() 骨机会 avec tes 电影。【参考方案2】:

如果我正确理解您的问题,您想调整过滤器。如果正确,试试这个过滤器:

ForEach(0..<self.userData.movies.filter self.showShowOnly ? $0.isShow == !self.showShowOnly : true .count, id: \.self)

当 self.showShowOnly = false 时,这将为您提供完整列表。

当 self.showShowOnly = true 时,会给你所有带有“isShow=false”的电影。

【讨论】:

谢谢你的回答,你是对的,这就是我想要的!我试过你的答案,但是当我测试时,我发现它不起作用......我认为这是因为它链接到 JSON 文件,但我有可能为每部电影更改 isShow 的值......这是可能的与 userData... 你知道为什么你的答案不起作用吗? (你可以在GitHub上下载我的项目并测试,我在里面添加你的代码行)

以上是关于如何在切换 SwiftUI 中使用 json 属性?的主要内容,如果未能解决你的问题,请参考以下文章

如何切换数组中对象的属性并更新 SwiftUI 视图?

如何在 SwiftUI 的 foreach 循环中设置切换状态

SwiftUI - 如何切换核心数据中的所有布尔值

如何在 SwiftUI 中触发状态更改/重绘切换更改

iOS:如何使用侧边菜单、SwiftUI 切换视图

SwiftUI - 如何在列表中编辑行?