Display data from JSON in alphabetical sections in Table View in Swift

后端 未结 1 1831
小鲜肉
小鲜肉 2021-01-17 01:23

I am pretty new to Xcode and Swift. I am trying to display data (restaurants names) in alphabetical order in TableView. I have a JSON file that sorts each restaurant into th

相关标签:
1条回答
  • 2021-01-17 02:02

    I tried to solve your problem using some different approach. Here is the Sample I made.
    Here is my controller code and tried to make it as similar to contact app as per the data provided by the API. I just took restaurant name in model as It will only be needed for sorting in alphabetical order. All the other details and explanation are mentioned in comments within the code.

    import UIKit
    
    class ViewController: UIViewController {
        @IBOutlet weak var restaurantsTableView: UITableView!
    
        //The main array for tableView
        var dataArray = [(String,[Restaurant])]()
    
        var indexTitles = [String]()
    
        override func viewDidLoad() {
            super.viewDidLoad()
            getData()
        }
    
        func getData() {
    
            let url = URL(string: "http://barhoppersf.com/json/neighborhoods.json")
            URLSession.shared.dataTask(with: url!) { (data, response, error) in
    
                guard let data = data else {return}
    
                let json = try? JSONSerialization.jsonObject(with: data, options: .mutableContainers) as! [String : AnyObject]
    
                guard let hoods = json?["hoods"] else { return }
    
                guard let names = hoods["neighborhoodNames"] as? [String:[AnyObject]] else { return }
    
                self.makeDataSource(names: names)
    
                DispatchQueue.main.async {
                    self.restaurantsTableView.reloadData()
                }
            }.resume()
        }
    
        // The main logic for sorting and making the data like Contacts App tableView
        func makeDataSource(names:[String:[AnyObject]]) {
            //Temporary array to hold restaurants on different indexes
            var dict = [String:[Restaurant]]()
    
            //Character set taken to check whether the starting key is alphabet or any other character
            let letters = NSCharacterSet.letters
    
            for (_,value) in names {
                //Iterating Restaurants
                for resObj in value {
                    if let restaurantName = resObj["name"] as? String {
                        let restaurant = Restaurant(name: restaurantName)
                        var key = String(describing: restaurant.name.characters.first!)
    
                        key = isKeyCharacter(key: key, letters: letters) ? key : "#"
    
                        if let keyValue = dict[key] {
                            //Already value exists for that key
                            var filtered = keyValue
                            filtered.append(restaurant)
    
                            //Sorting of restaurant names alphabetically
                            filtered = filtered.sorted(by: {$0.0.name < $0.1.name})
                            dict[key] = filtered
                        } else {
                            let filtered = [restaurant]
                            dict[key] = filtered
                        }
                    }
                }
            }
            //To sort the key header values
            self.dataArray = Array(dict).sorted(by: { $0.0 < $1.0 })
    
            //Logic to shift the # category to bottom
            let temp = self.dataArray[0]
            self.dataArray.removeFirst()
            self.dataArray.append(temp)
    
            //For setting index titles
            self.indexTitles = Array(dict.keys.sorted(by: <))
    
            //Making the index title # at the bottom
            let tempIndex = self.indexTitles[0]
            self.indexTitles.removeFirst()
            self.indexTitles.append(tempIndex
        }
    }
    
    //Function to check whether key is alphabet or not
    func isKeyCharacter(key:String,letters:CharacterSet) -> Bool {
        let range = key.rangeOfCharacter(from: letters)
        if let _ = range {
            //Your key is an alphabet
            return true
        }
        return false
    }
    
    extension ViewController: UITableViewDataSource, UITableViewDelegate {
    
        func numberOfSections(in tableView: UITableView) -> Int {
            return dataArray.count
        }
    
        func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
            return dataArray[section].1.count
        }
    
        func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
            let cell = tableView.dequeueReusableCell(withIdentifier: "RestaurantsTableCell") as! RestaurantsTableCell
            let restaurant = dataArray[indexPath.section].1[indexPath.row]
            cell.restaurantNameLabel.text = restaurant.name
            return cell
        }
    
        func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    
            //Pass this model to detail VC
            let restaurant = dataArray[indexPath.section].1[indexPath.row]
    
            let detailVC = self.storyboard?.instantiateViewController(withIdentifier: "DetailViewController") as! DetailViewController
            detailVC.restaurant = restaurant
            self.navigationController?.pushViewController(detailVC, animated: true)
        }  
    
        func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
            return 30
        }
    
        func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
            return dataArray[section].0
        }
    
        //For index titles
        func sectionIndexTitles(for tableView: UITableView) -> [String]? {
            return self.indexTitles
        }
    }
    

    The Restaurant class is now separated. Also modified the Sample in the link and created a new DetailViewController class.

    import Foundation
    
    class Restaurant {
        var name = ""
    
        init(name:String) {
            self.name = name
        }
    }
    

    This code can be improved in the sort logic. Improvements are welcome.

    Here is the Output:


    Here is the detail controller:

    0 讨论(0)
提交回复
热议问题