簡體   English   中英

Swift - 顯示每個 tableview 部分的特定數據

[英]Swift - display specific data for each tableview section

我正在使用 CocktailDB。 通過創建一個請求,我得到一個 JSON 文件,使用可解碼協議對其進行解析。 從 JSON 我得到所有飲料的類別並將它們顯示為我的表格視圖的部分。

在每個 tableview 部分中,我想顯示特定類別的飲料(部分的標題)。 類別中的每個部分單元格一杯飲料(飲料的 strDrink(名稱)和 strDrinkThumb(圖像))。

我有一個方法可以創建從特定類別獲取飲料的請求 - getDrinksFrom(category: String)。
請建議我如何在特定部分調用此方法來獲取和顯示本部分中特定類別的飲料?

我的代碼:

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)
    }
}

類別 Model:

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

struct Category:Decodable {
    var strCategory: String
}

喝Model:

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

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

我所知道的:

JSON結構: 在此處輸入圖像描述

我的建議是為這些部分創建一個帶有名稱和飲料的自定義結構Category 它不符合Decodable ,這是有意的

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

和適當的數據源數組

var categories = [Category]()

然后使用傳統的JSONSerialization加載和解析類別,並通過映射名稱來填充數組。 進一步添加完成處理程序

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()
}

為了避免命名混亂(太多飲料)命名根結構Response

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

加載與類別相關的數據並將飲料數組分配給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")
    }
}

最后更改表視圖數據源方法以匹配節結構

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
    }
}

您還可以將這兩個功能放在一起並加載所有類別的所有飲料

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()
}

這只是一個偽代碼,它將讓您了解如何進一步進行。 代碼未經測試。

創建要加載的部分數組。

var sections: [Sections] = []

在您的 tableview 委托中,您可以為需要加載的部分創建一個結構,這將幫助您識別單元格中的部分以獲取行索引路徑,您可以在其中根據類別調用 API。

extension ViewController: UITableViewDataSource, UITableViewDelegate {
    
    struct Sections {
        static var count = 0
        // In stantiate table view headers index order
        enum SectionType {
            case SoftDrink
            case OrdinaryDrink
            case MilkShake
        }
        
        var type: SectionType?
        var section: Int?
        var rows: Int?
    }
    
    func setUpTableView() {
        // Set Up Tableview Data
        if check if Drink is type of SoftDrink /*If you sections are loaded dynamic u can add condition*/ {
            sections.append(Sections(type: .SoftDrink, section: Sections.count, rows: 1))
            Sections.count += 1
        }
        Sections.count = 0
    }

    
    func numberOfSections(in _: UITableView) -> Int {
        sections.count
    }
    
    func tableView(_: UITableView, numberOfRowsInSection section: Int) -> Int {
        sections[section].rows ?? 0
    }
    
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        var tableCell: UITableViewCell = UITableViewCell()
        guard let type = sections[indexPath.section].type else {
            tableCell.selectionStyle = .none
            return tableCell
        }
        switch type {
        case .SoftDrink: break
        // Instantiate cell and API calls.
        case .OrdinaryDrink: break
        // Instantiate cell and API calls.
        case .MilkShake: break
            // Instantiate cell and API calls.
        }
        tableCell.selectionStyle = .none
        
        return tableCell
    }
    
}

setUpTableView()可以在 viewDidLoad 方法中調用。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM