Swift - 显示每个 tableview 部分的特定数据 [英] Swift - display specific data for each tableview section

查看:34
本文介绍了Swift - 显示每个 tableview 部分的特定数据的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用 CocktailDB.通过创建一个请求,我得到一个 JSON 文件,用可解码协议解析它.我从 JSON 中获取所有饮料的类别并将它们显示为我的 tableview 的部分.

I'm working with CocktailDB. By creating a request I get a JSON file, parse it with Decodable protocol. From JSON I get all drinks' categories and display them as the sections of my tableview.

在每个 tableview 部分中,我想显示来自特定类别(部分标题)的饮料.来自类别(饮料的 strDrink(名称)和 strDrinkThumb(图像))中的每个部分单元格一种饮料.

In each tableview section I want to display drinks from specific category (section's header). One drink per section cell from the category (drink's strDrink (name) and strDrinkThumb (image)).

我有一个方法可以创建从特定类别获取饮料的请求 - getDrinksFrom(category: String).
请建议我如何为特定部分调用此方法以获取和显示此部分中特定类别的饮料?

I have a method that creates a request to get drinks from specific category - getDrinksFrom(category: String).
Please advice how can I call this method for specific section to get and display drinks from specific category in this section?

我的代码:

class ViewController: UIViewController {
    
    var drinks = [Drink]()
    var categories = [Category]()
    
    @IBOutlet weak var tableView: UITableView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        getCategories()
        getDrinksFrom(category: "Cocoa")
    }
    
    func getCategories() {
        let url = URL(string: "https://www.thecocktaildb.com/api/json/v1/1/list.php?c=list")
        
        URLSession.shared.dataTask(with: url!) { (data, response, error) in
            
            if error == nil {
                do {
                    self.categories = try JSONDecoder().decode(Categories.self, from: data!).drinks
                    
                    DispatchQueue.main.async {
                        self.tableView.reloadData()
                    }
                    print(self.categories)
                    
                } catch {
                    print(error)
                }
            }
        }.resume()
    }
    
    func getDrinksFrom(category: String) {
        let url = URL(string: "https://www.thecocktaildb.com/api/json/v1/1/filter.php?c=\(category)")
        
        URLSession.shared.dataTask(with: url!) { (data, response, error) in
            
            if error == nil {
                do {
                    self.drinks = try JSONDecoder().decode(Drinks.self, from: data!).drinks
                    
                    DispatchQueue.main.async {
                        self.tableView.reloadData()
                    }
                    print(self.drinks)
                    
                } catch {
                    print(error)
                }
            }
        }.resume()
    }
    
}

extension ViewController: UITableViewDataSource, UITableViewDelegate {
    
    func numberOfSections(in tableView: UITableView) -> Int {
        return categories.count
    }
    
    func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
        return categories[section].strCategory
    }
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 2
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "drinkCell") as! DrinkCell
        
        cell.drinkName.text = drinks[indexPath.row].strDrink
        
        let url = drinks[indexPath.row].strDrinkThumb
        cell.drinkImage.downloaded(from: url)
        
        return cell
    }
}

// to download an image from web
extension UIImageView {
    func downloaded(from url: URL, contentMode mode: UIView.ContentMode = .scaleAspectFit) {
        contentMode = mode
        URLSession.shared.dataTask(with: url) { data, response, error in
            guard
                let httpURLResponse = response as? HTTPURLResponse, httpURLResponse.statusCode == 200,
                let mimeType = response?.mimeType, mimeType.hasPrefix("image"),
                let data = data, error == nil,
                let image = UIImage(data: data)
                else { return }
            DispatchQueue.main.async() { [weak self] in
                self?.image = image
            }
        }.resume()
    }
    
    func downloaded(from link: String, contentMode mode: UIView.ContentMode = .scaleAspectFit) {
        guard let url = URL(string: link) else { return }
        downloaded(from: url, contentMode: mode)
    }
}

类别模型:

struct Categories:Decodable {
    var drinks: [Category]
}

struct Category:Decodable {
    var strCategory: String
}

饮料型号:

struct Drinks:Decodable {
    var drinks: [Drink]
}

struct Drink:Decodable {
    var strDrink: String
    var strDrinkThumb: String
}

我所知道的:

JSON 结构:

推荐答案

我的建议是创建一个自定义结构 Category,其中包含部分的名称和饮品.它不符合Decodable,这是故意的

My suggestion is to create a custom struct Category with name and drinks for the sections. It does not conform to Decodable, this is intended

struct Category {
    let name : String
    var drinks : [Drink]
}

和适当的数据源数组

var categories = [Category]()

然后使用传统的 JSONSerialization 加载和解析类别,并通过映射名称填充数组.进一步添加一个完成处理程序

then load and parse the categories with traditional JSONSerialization and populate the array by mapping the names. Further add a completion handler

func getCategories(completion: @escaping () -> Void) {
    let url = URL(string: "https://www.thecocktaildb.com/api/json/v1/1/list.php?c=list")
    
    URLSession.shared.dataTask(with: url!) { (data, response, error) in
        
        if let error = error { print(error); return }
        do {
            let result = try JSONSerialization.jsonObject(with: data!) as! [String:Any]
            let categoryNames = result["drinks"] as! [[String:String]]
            self.categories = categoryNames.map{ Category(name: $0["strCategory"]!, drinks:[])}
            completion()
            
        } catch {
            print(error)
        }
    }.resume()
}

为了避免命名混乱(太多drinks)命名根结构Response

To avoid naming confusion (too many drinks) name the root struct Response

struct Response : Decodable {
    let drinks: [Drink]
}

加载一个类别相关的数据,并将drinks数组赋值给categories

Load the data related to a category and assign the drinks array to the corresponding array in categories

func getDrinksFrom(category: String) {
    let url = URL(string: "https://www.thecocktaildb.com/api/json/v1/1/filter.php?c=\(category)")
    
    URLSession.shared.dataTask(with: url!) { (data, response, error) in
        
        if let error = error { print(error); return }
        do {
            let drinks = try JSONDecoder().decode(Response.self, from: data!).drinks
            guard let index = categories.firstIndex(where: {$0.name == category}) else { return }
            self.categories[index].drinks = drinks
            DispatchQueue.main.async {
                self.tableView.reloadData()
            }
            
        } catch {
            print(error)
        }
    }.resume()
}

并将 viewDidLoad 替换为

override func viewDidLoad() {
    super.viewDidLoad()
    getCategories { [weak self] in
        self?.getDrinksFrom(category: "Cocoa")
    }
}

最后修改table view数据源方法来匹配section结构

Finally change the table view data source methods to match the section structure

extension ViewController: UITableViewDataSource, UITableViewDelegate {
    
    func numberOfSections(in tableView: UITableView) -> Int {
        return categories.count
    }
    
    func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
        return categories[section].name
    }
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return categories[section].drinks.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "drinkCell") as! DrinkCell
        
        let category = categories[indexPath.section]
        let drink = category.drinks[indexPath.row]
        cell.drinkName.text = drink.strDrink
        
        let url = drink.strDrinkThumb
        cell.drinkImage.downloaded(from: url)
        
        return cell
    }
}


您也可以将这两个功能放在一起,加载所有类别的所有饮品


You can also put both functions together and load all drinks for all categories

func loadAllCategories() {
    let url = URL(string: "https://www.thecocktaildb.com/api/json/v1/1/list.php?c=list")
    
    URLSession.shared.dataTask(with: url!) { (data, response, error) in
        
        if let error = error { print(error); return }
        do {
            let result = try JSONSerialization.jsonObject(with: data!) as! [String:Any]
            let categoryNames = (result["drinks"] as! [[String:String]]).map{$0["strCategory"]!}
            let group = DispatchGroup()
            for category in categoryNames {
                let categoryURLString = "https://www.thecocktaildb.com/api/json/v1/1/filter.php?c=\(category)".addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!
                let categoryURL = URL(string: categoryURLString)!
                group.enter()
                let categoryTask = URLSession.shared.dataTask(with: categoryURL) { (categoryData, _, categoryError) in
                    defer { group.leave() }
                    if let categoryError = categoryError { print(categoryError); return }
                    do {
                        let drinks = try JSONDecoder().decode(Response.self, from: categoryData!).drinks
                        self.categories.append(Category(name: category, drinks: drinks))
                    } catch {
                        print(error)
                    }
                }
                categoryTask.resume()
                
            }
            group.notify(queue: .main) {
                self.tableView.reloadData()
            }
            
        } catch {
            print(error)
        }
    }.resume()
}

这篇关于Swift - 显示每个 tableview 部分的特定数据的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆