【问题标题】:Dynamically populating cells depending on page title根据页面标题动态填充单元格
【发布时间】:2017-01-24 21:53:24
【问题描述】:

我正在尝试让程序检查导航标题,然后相应地填充单元格。这对硬编码数组来说很容易,但使用 Core Data 对象会让事情变得很困难。

初始化程序 - 每个公司的 3 个产品和一个空数组,在检查页面标题后将它们设置为:

let defaultProductsApple = ["iPhone", "iPad", "Macbook"]
let defaultProductsGoogle = ["Search", "Firebase", "Magic Leap"]
let defaultProductsFacebook = ["Facebook", "Instagram", "WhatsApp"]
let defaultProductsTesla = ["Model S", "Model X", "PowerWall"]
let defaultProductsTwitter = ["Twitter", "Periscope", "Vine"]
var defaultProducts = [String]()

viewDidLoad - 检查页面标题并填充空数组,然后在删除时设置为 Core Data 以保持持久性:

override func viewDidLoad() {
    super.viewDidLoad()

    tableView.register(Cell.self, forCellReuseIdentifier: cellId)

    // Navbar stuff
    navigationItem.leftBarButtonItem = UIBarButtonItem(title: "Back", style: .plain, target: self, action: #selector(handleBackButton))
    navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Add", style: .plain, target: self, action: #selector(handleAdd))

        let entity = NSEntityDescription.entity(forEntityName: "Product", in: managedContext)!

        switch self.navigationItem.title! {
        case "Apple":
            defaultProducts = defaultProductsApple
        case "Google":
            defaultProducts = defaultProductsGoogle
        case "Twitter":
            defaultProducts = defaultProductsTwitter
        case "Tesla":
            defaultProducts = defaultProductsTesla
        case "Facebook":
            defaultProducts = defaultProductsFacebook
        default:
            defaultProducts = defaultProductsApple
        }

        for productName in defaultProducts {
            let product = NSManagedObject(entity: entity,insertInto: managedContext)
            product.setValue(productName, forKey: "name")
            products.append(product)
        }
        do {
            try managedContext.save()
            tableView.reloadData()
        } catch let error as NSError {
            print("Could not save. \(error), \(error.userInfo)")
        }

}

最后在 cellForRow 中设置标签:

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath) as! Cell
    let product = products[indexPath.row]

    cell.textLabel?.text = product.value(forKey: "name") as? String

    return cell
}

单元格在第一次尝试时会按应有的方式填充 - 即,如果我在上一个屏幕中点击“Apple”,单元格会显示 Apple 的 3 种产品。但是,如果我返回并点击“Google”,单元格现在会显示 Apple 的 3 款产品以及 Google 的 3 款产品,以此类推。

我怎样才能让cell.textLabel?.text = product.value(forKey: "name") as? String 只加载该公司的产品?

编辑:viewWillAppearsave 方法:

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)

    //1
    guard let appDelegate =
        UIApplication.shared.delegate as? AppDelegate else {
            return
    }

    let managedContext =
        appDelegate.persistentContainer.viewContext

    //2
    let fetchRequest =
        NSFetchRequest<NSManagedObject>(entityName: "Product")

    //3
    do {
        products = try managedContext.fetch(fetchRequest)
    } catch let error as NSError {
        print("Could not fetch. \(error), \(error.userInfo)")
    }
}




func save(name: String) {

    guard let appDelegate =
        UIApplication.shared.delegate as? AppDelegate else {
            return
    }

    let managedContext =
        appDelegate.persistentContainer.viewContext

    let entity =
        NSEntityDescription.entity(forEntityName: "Product",
                                   in: managedContext)!

    let product = NSManagedObject(entity: entity,
                                  insertInto: managedContext)

    product.setValue(name, forKey: "name")

    do {
        try managedContext.save()
        products.append(product)
    } catch let error as NSError {
        print("Could not save. \(error), \(error.userInfo)")
    }
}

最终编辑 - 一次性设置所有默认值

// Default 5 companies at launch - user can delete/add/edit
func setDefaults() {

    let userDefaults = UserDefaults.standard
    let defaultValues = ["firstRun" : true]
    userDefaults.register(defaults: defaultValues)

    if userDefaults.bool(forKey: "firstRun") {

        let defaultProducts = ["Apple" : ["iPhone", "iPad", "Macbook"],
                               "Google" : ["Search", "Firebase", "Magic Leap"],
                               "Facebook" : ["Facebook", "Instagram", "WhatsApp"],
                               "Tesla" : ["Model S", "Model X", "PowerWall"],
                               "Twitter" : ["Twitter", "Periscope", "Vine"]]

        let companyEntity = NSEntityDescription.entity(forEntityName: "Company", in: managedContext)!
        let productEntity = NSEntityDescription.entity(forEntityName: "Product", in: managedContext)!


        // Setting the default company data (name, logo, and stockPrice)
        let url = URL(string: "https://query.yahooapis.com/v1/public/yql?q=select%20symbol%2C%20Ask%2C%20YearHigh%2C%20YearLow%20from%20yahoo.finance.quotes%20where%20symbol%20in%20(%22AAPL%22%2C%22GOOG%22%2C%22TWTR%22%2C%22TSLA%22%2C%20%22FB%22)&format=json&env=store%3A%2F%2Fdatatables.org%2Falltableswithkeys")!
        let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
            if error != nil {
                print(error!)
            } else if let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 {
                let json = JSON(data: data!)
                if let quotes = json["query"]["results"]["quote"].array {
                    for quote in quotes {
                        let symbol = quote["symbol"].stringValue
                        let name = NSLocalizedString(symbol, comment:"")
                        let ask = quote["Ask"].stringValue
                        let company = NSManagedObject(entity: companyEntity,insertInto: managedContext)
                        company.setValue(name, forKey: "name")
                        company.setValue(name, forKey: "logo")
                        company.setValue(ask, forKey: "stockPrice")
                        companies.append(company)
                        print(json)
                    }

                    // Setting the default products - should I be doing this here too?
                    let companyProducts = defaultProducts[companyName]

                    // iterate through all those products names...
                    for productName in companyProducts {
                        // create the corresponding Product:
                        let product = NSManagedObject(entity: productEntity,insertInto: managedContext)
                        // set its name attribute...
                        product.setValue(productName, forKey: "name")
                        // then set the relationship to the relevant Company...
                        product.setValue(company, forKey: "company")
                    }

                    DispatchQueue.main.async {
                        do {
                            try managedContext.save()
                            vc.tableView.reloadData()
                            userDefaults.set(false, forKey: "firstRun")
                        } catch let error as NSError {
                            print("Could not save. \(error), \(error.userInfo)")
                        }
                    }
                }
            } else {
                print("The data couldn't be loaded")
            }
        }
        task.resume()
    }
}

【问题讨论】:

  • 您是否有任何其他代码可以更新products 数组,可能是通过执行获取请求,例如。在viewWillAppear 或其他生命周期方法之一?
  • @pbasdf 是的,我编辑了我的问题以包括viewWillAppear 以及save 方法。任何帮助将不胜感激!
  • 谢谢 - 这解释了你所观察到的。获取请求(在 viewWillAppear 中)获取所有内容;您需要一些方法来区分 Apple 的产品与 Google(以及其他所有人的)的产品。您可以通过向 Product 实体添加“公司”属性或向“公司”实体添加关系来做到这一点。您还需要修改代码以仅加载这些默认值一次。稍后我会添加完整的答案...
  • @pbasdf 谢谢,我会准备好在您发布该答案后立即投票!我刚刚开始学习 Core Data,所以这将是一次很棒的学习体验。我一直想在我的 Company 和 Product 实体之间添加关系,但不确定如何使用它或它对我有什么好处。如果有帮助,我已经发布了我的数据模型的屏幕截图。

标签: ios swift xcode uitableview core-data


【解决方案1】:

首先创建从CompanyProduct 的关系,并将其命名为“产品”。每个Company 可以有多个Products,因此使关系为“对多”。同样创建从ProductCompany 的关系,并将其命名为“公司”。每个Product 只与一个Company 相关(我假设),因此将关系设为“一对一”,并将其逆设置为“产品”。

为了让生活更轻松,请在创建默认 Company 数据的同时添加默认值(不确定您现在在哪里执行此操作)。请注意,我已将您的五个产品数组更改为字典 (defaultProducts),并以公司名称作为键:这样更容易一次性加载到 CoreData:

func setDefaults() {
    let userDefaults = UserDefaults.standard
    let defaultValues = ["firstRun" : true]
    userDefaults.register(defaults: defaultValues)

    if userDefaults.bool(forKey: "firstRun") {
        let defaultProducts = ["Apple" : ["iPhone", "iPad", "Macbook"],
                               "Google" : ["Search", "Firebase", "Magic Leap"],
                               "Facebook" : ["Facebook", "Instagram", "WhatsApp"],
                               "Tesla" : ["Model S", "Model X", "PowerWall"],
                               "Twitter" : ["Twitter", "Periscope", "Vine"]]

        let companyEntity = NSEntityDescription.entity(forEntityName: "Company", in: managedContext)!
        let productEntity = NSEntityDescription.entity(forEntityName: "Product", in: managedContext)!

        // Setting the default company data (name, logo, and stockPrice)
        let url = URL(string: "https://query.yahooapis.com/v1/public/yql?q=select%20symbol%2C%20Ask%2C%20YearHigh%2C%20YearLow%20from%20yahoo.finance.quotes%20where%20symbol%20in%20(%22AAPL%22%2C%22GOOG%22%2C%22TWTR%22%2C%22TSLA%22%2C%20%22FB%22)&format=json&env=store%3A%2F%2Fdatatables.org%2Falltableswithkeys")!
        let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
            if error != nil {
                print(error!)
            } else if let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 {
                let json = JSON(data: data!)
                if let quotes = json["query"]["results"]["quote"].array {
                    for quote in quotes {
                        let symbol = quote["symbol"].stringValue
                        let name = NSLocalizedString(symbol, comment:"")
                        let ask = quote["Ask"].stringValue
                        let company = NSManagedObject(entity: companyEntity,insertInto: managedContext)
                        company.setValue(name, forKey: "name")
                        company.setValue(name, forKey: "logo")
                        company.setValue(ask, forKey: "stockPrice")
                        companies.append(company)
                        // Move the default products code here:
                        let companyProducts = defaultProducts[name]
                        // iterate through all those products names...
                        for productName in companyProducts {
                            // create the corresponding Product:
                            let product = NSManagedObject(entity: productEntity, insertInto:managedContext)
                            // set its name attribute...
                            product.setValue(productName, forKey: "name")
                            // then set the relationship to the relevant Company...
                            product.setValue(company, forKey: "company")
                        }

                        print(json)
                    }

                    DispatchQueue.main.async {
                        do {
                            try managedContext.save()
                            vc.tableView.reloadData()
                            userDefaults.set(false, forKey: "firstRun")
                        } catch let error as NSError {
                            print("Could not save. \(error), \(error.userInfo)")
                        }
                    }
                }
            } else {
                print("The data couldn't be loaded")
            }
        }
        task.resume()
    }
}

在您的视图控制器中,您可以从viewDidLoad 中删除设置默认产品的代码。

您当前正在使用导航项标题来确定您应该显示哪个公司的产品。所以在viewWillAppear,你需要修改你的fetch来限制结果。使用predicate:

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    //1
    guard let appDelegate =
        UIApplication.shared.delegate as? AppDelegate else {
            return
    }
    let managedContext =
        appDelegate.persistentContainer.viewContext
    //2
    let companyToDisplay = self.navigationItem.title!
    let fetchRequest =
        NSFetchRequest<NSManagedObject>(entityName: "Product")
    fetchRequest.predicate = NSPredicate(format:"company.name == %@",companyToDisplay)    
    //3
    do {
        products = try managedContext.fetch(fetchRequest)
    } catch let error as NSError {
        print("Could not fetch. \(error), \(error.userInfo)")
    }
}

【讨论】:

  • 您现在是我的英雄,非常感谢您提供如此详尽的回答!绝对可以帮助我了解如何更有效地使用 Core Data!
  • 快速更新 - 我正在尝试将此解决方案应用到我设置默认公司的同一位置(应用程序启动时使用这 5 个默认公司,每个公司有 3 个产品,然后让用户删除并根据需要添加)。我喜欢使用字典来分配每个公司的产品的想法,我只是不知道如何将它应用到我的功能中。如果您想看一下(或者我可以提出一个单独的问题),我将setDefaults() 功能粘贴在我的问题底部,但是如果您没有时间,请不要担心,您已经已经有很大的帮助了!
  • @d0xi45 我已经编辑了我的答案 - 你非常接近:设置产品需要在 for quote in quotes {} 循环内,以便为每个公司设置它们。
  • 好的,现在一切正常!尽管出于某种原因,我必须运行该应用程序两次才能显示默认设置(只运行一次会显示一个空的表格视图,然后再次运行所有内容),但我会及时解决。再次感谢您指导我完成这件事,我会尽我所能在我提高自己的技能时付出代价!
【解决方案2】:

在您执行switch self.navigationItem.title! { 之前,请像这样重置您的数组:

defaultProducts.removeAll()

您的 viewController 可能没有被释放,因此 defaultProducts 数组保留了它之前的项目。通过在填充之前清除它,该问题应该会消失。

【讨论】:

  • 我刚试过这个(在 switch 语句之前输入viewDidLoad),不幸的是它没有工作。
  • 我返回products.count,产品为var products = [NSManagedObject]()
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-01-13
  • 2015-03-29
  • 2020-10-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多