【问题标题】:SwiftUI how to filter a dynamic array populate from a json fileSwiftUI如何过滤从json文件填充的动态数组
【发布时间】:2020-01-16 03:12:59
【问题描述】:

我是 Swift 和 SwiftUI 的新手。

我有一个 RecipeLinkFetcher() 动态获取一个 json 文件并填充一组配方对象。 我正在尝试根据类别过滤 RecipeLink 对象的数组。

RecipeLink 定义为

struct RecipeLink: Codable, Identifiable 
{
    public var id: Int
    public var category: String
    public var title: String
    public var permalink: String
    public var excerpt: String
    public var image: String

    enum CodingKeys: String, CodingKey {
        case id = "id"
        case category = "category"
        case title = "title"
        case permalink = "permalink"
        case excerpt = "excerpt"
        case image = "featured_image"
    }
}

在我看来,我正在使用下面的代码来填充包含 RecipeLink 数组的变量 fetcher

@ObservedObject var fetcher = RecipeLinkFetcher()
var categoryName: String = "Featured"

我正在尝试使用下面的方法将其过滤为一个新变量,但这不起作用

var recipes = fetcher.filter($0.category == categoryName)

这是我的 RecipeLinkFetcher

import Foundation

public class RecipeLinkFetcher: ObservableObject {

    @Published var recipeLink:[RecipeLink] = [RecipeLink]()

    init(){
        load()
    }

    func load() {
     let url = URL(string: "http://localhost/wordpress2/wp-content/uploads/json-export.json")!

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

        }.resume()

    }
}


struct RecipeLink: Codable, Identifiable {
    public var id: Int
    public var category: String
    public var title: String
    public var permalink: String
    public var excerpt: String
    public var image: String

    enum CodingKeys: String, CodingKey {
        case id = "id"
        case category = "category"
        case title = "title"
        case permalink = "permalink"
        case excerpt = "excerpt"
        case image = "featured_image"
    }
}

这是我的 UIView,标有 ** 的行我试图过滤数组/列表

struct CategoryRow: View {

    @ObservedObject var fetcher = RecipeLinkFetcher()
    var categoryName: String

    **var filteredList = $fetcher.filter({$0.category==categoryName})**

    var body: some View {
        VStack() {
            Text(self.categoryName.htmlUnescape())
                .font(.headline)
                .padding(.leading, 15)
                .padding(.top, 5)
           // ScrollView(.horizontal, showsIndicators: false) {
                List(fetcher.recipeLink){ recipe in
                    if(recipe.category.htmlUnescape() == self.categoryName){
                        HStack(alignment: .top, spacing: 0 ){
                            CategoryItem(imageURL: recipe.image, recipeTitle: recipe.title)
                    }
                }
            }
        }
    }
}

【问题讨论】:

  • 试试var recipes = fetcher.filter { $0.category == categoryName }

标签: arrays swift object filter swiftui


【解决方案1】:

将过滤后的数组移动到 ObseravbleObject 上的 @Published,以便在视图发生变化时重建视图。在observedObject 上将类别名称设为CurrentValueSubject,并将其与您的网络结果相结合,以反应性地导出过滤后的数组。即,当您更改 categoryName 的 .value 或获取 recipeLink 的新值时,filteredRecipeLinks 将自动重新计算,并且由于它是 @Published 属性,您的视图将自动重建。

public class RecipeLinkFetcher: ObservableObject {

    @Published var recipeLink:[RecipeLink] = [RecipeLink]()
    @Published var filteredRecipeLinks:[RecipeLink] = []
    var categoryName = CurrentValueSubject<String, Never>("")
    private var subscriptions = Set<AnyCancellable>()
    init(){
        load()
        Publishers
          .CombineLatest($recipeLink, categoryName)
          .map { combined -> [RecipeLink] in
            let (links, name) = combined
            guard !name.isEmpty else { return links }  // Show all when search term is empty
            return links.filter { $0.category == categoryName }
          }
          .assign(to: \.recipeLink, on: self)
          .store(in: &subscriptions)
    }
...
}

【讨论】:

  • 感谢@Josh Homann,我按照您的指出进行了更改。我现在在 return links.filter { $0.category == categoryName } 处的 RecipeLinkFetcher 中遇到一个新错误,它是二进制运算符“==”不能应用于“字符串”和“”类型的操作数CurrentValueSubject'。不知道如何解决这个问题。再次感谢您的帮助
  • currentValueSubject 是一个发布者,它会记住其上次发布的值(在本例中为字符串)。您需要调用它的.value 来获取当前字符串。 return links.filter { $0.category == categoryName.value }
  • 谢谢@Josh Homann 我做了你提到的改变,但没有运气,它仍然返回一个没有项目的过滤RecipeLinks 数组。我添加了一个断点来查看数组中返回的值。
【解决方案2】:

我将分离获取的模型并呈现过滤后的食谱,如下面的方法(scratchy)

@State private var filteredList = [RecipeLink]() // to be presented

var body: some View {
    VStack() {
        Text(self.categoryName.htmlUnescape())
            .font(.headline)
            .padding(.leading, 15)
            .padding(.top, 5)

        List(filteredList) { recipe in // list only filtered
            HStack(alignment: .top, spacing: 0 ){
                CategoryItem(imageURL: recipe.image, recipeTitle: recipe.title)
        }
        .onReceive(fetcher.$recipeLink) { _ in // once model updated refilter
            self.filteredList = self.fetcher.recipeLink.filter 
                         {$0.category == self.categoryName}
        }
    }
}

【讨论】:

  • 这似乎不起作用 没有显示任何项目 不知道为什么...感谢您的帮助
  • 我没有你的模型,所以无法测试返回的数据 - 只是更新了过滤条件。您可以在onReceive 中设置断点来查看哪些记录来了,哪些被过滤了。
  • 感谢Asperi,模型也在帖子的顶部。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-08-15
  • 2014-08-18
  • 2023-03-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多