Как отобразить файл JSON в разделах и строках в TableView в Swift

Я новичок в Xcode и Swift. Мне нужна помощь в отображении данных из файла JSON в TableView с разделами и строками. Мне нужно отобразить разные рестораны в каждом районе. Я думаю, что мне нужно внести изменения в мой файл JSON, но я не могу понять это. Я действительно буду признателен за любую помощь. Ваше здоровье.

Это мой файл JSON:

{
"hoods": {

"neighborhoodNames": {
   "marina":[

{
  "name": "MARINA-1",
  "dob": "December 18, 1963",
  "image": "http://microblogging.wingnity.com/JSONParsingTutorial/brad.jpg"
},
{
  "name": "MARINA-2",
  "description": "Tom Cruise, is an American film actor and producer. He has been nominated for three Academy Awards and has won three Golden Globe Awards. He started his career at age 19 in the 1981 film Endless Love.",
  "dob": "July 3, 1962",
  "image": "http://microblogging.wingnity.com/JSONParsingTutorial/cruise.jpg"
},
{
  "name": "MARINA-3",
  "description": "John Christopher 'Johnny' Depp II is an American actor, film producer, and musician. He has won the Golden Globe Award and Screen Actors Guild award for Best Actor.",
  "dob": "June 9, 1963",
  "image": "http://microblogging.wingnity.com/JSONParsingTutorial/johnny.jpg"
}
],
   "MISSION":[

{
  "name": "MISSION-1",
  "dob": "December 18, 1963",
  "image": "http://microblogging.wingnity.com/JSONParsingTutorial/brad.jpg"
},
{
  "name": "MISSION-2",  
  "dob": "July 3, 1962",
  "image": "http://microblogging.wingnity.com/JSONParsingTutorial/cruise.jpg"
},
{
  "name": "MISSION-3",
  "dob": "June 9, 1963",
  "image": "http://microblogging.wingnity.com/JSONParsingTutorial/johnny.jpg"
},
 {
  "name": "MISSION-4",
  "dob": "June 9, 1963",
  "image": "http://microblogging.wingnity.com/JSONParsingTutorial/johnny.jpg"
}
]}
}
}

Это ссылка на файл JSON: http://barhoppersf.com/json/hoods.json

Это мой Xcode:

import UIKit

class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {

  let urlString = "http://barhoppersf.com/json/hoods.json"



@IBOutlet weak var tableView: UITableView!

var nameArray:[[String]] = []
var dobArray:[[String]] = []
var imgURLArray:[[String]] = []
var neighborhoodNames = [String]()


override func viewDidLoad() {
    super.viewDidLoad()

    tableView.delegate = self
    tableView.dataSource = self


    self.downloadJsonWithURL()

    // Do any additional setup after loading the view, typically from a nib.
}

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
}




func downloadJsonWithURL() {

    let url = NSURL(string: urlString)

    URLSession.shared.dataTask(with: (url as URL?)!, completionHandler: {(data, response, error) -> Void in


        if let jsonObj = try? JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? NSDictionary {


            if let actorArray = jsonObj!.value(forKey: "hoods") as? NSArray {

                for actor in actorArray{

                    if let actorDict = actor as? NSDictionary {


                        if let name = actorDict.value(forKey: "neighborhoodNames") {
                            self.neighborhoodNames.append(name as! String)
                        }
                        if let name = actorDict.value(forKey: "name") {
                            self.nameArray.append([name as! String])
                        }
                        if let name = actorDict.value(forKey: "dob") {
                            self.dobArray.append([name as! String])
                        }
                        if let name = actorDict.value(forKey: "image") {
                            self.imgURLArray.append([name as! String])
                        }

                    }
                }
            }

//                self.nameArray = self.nameArray.sorted()


            OperationQueue.main.addOperation({
                self.tableView.reloadData()
            })
        }
    }).resume()
}




 func downloadJsonWithTask() {

    let url = NSURL(string: urlString)

    var downloadTask = URLRequest(url: (url as URL?)!, cachePolicy:     URLRequest.CachePolicy.reloadIgnoringCacheData, timeoutInterval: 15)

    downloadTask.httpMethod = "GET"

    URLSession.shared.dataTask(with: downloadTask, completionHandler: {(data,   response, error) -> Void in

         let jsonData = try? JSONSerialization.jsonObject(with: data!, options:  .allowFragments)

        print(jsonData as Any)

    }).resume()
}


   // MARK: - changing the color, background and position of the header
    func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int)  -> UIView? {
    let headerView = UIView()
    headerView.backgroundColor = UIColor.self.init(red: 254/255, green:  170/255, blue: 25/255, alpha: 1.0)


    let headerLabel = UILabel(frame: CGRect(x: 8, y: 5, width: tableView.bounds.size.width, height: tableView.bounds.size.height))

    headerLabel.font = UIFont(name: "Trebuchet MS", size: 15)
    headerLabel.textColor = UIColor.darkGray
    headerLabel.text = self.tableView(self.tableView, titleForHeaderInSection: section)
    headerLabel.sizeToFit()
    headerView.addSubview(headerLabel)

    return headerView
}


    // MARK: - changing the size of the header cell
    //      func tableView(_ tableView: UITableView, heightForHeaderInSection  section: Int) -> CGFloat {
    //        return 40
    //    }


       func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

      return (nameArray[section].count)

}



     func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {

    return neighborhoodNames[section]
}

     func numberOfSections(in tableView: UITableView) -> Int {
    return neighborhoodNames.count
}


    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

    let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as! TableViewCell

      //          cell.nameLabel.text = nameArray[indexPath.row]
         cell.nameLabel?.text = nameArray[indexPath.section][indexPath.row]


    return cell
}

  // set up A_Z index
   //    func sectionIndexTitles(for tableView: UITableView) -> [String]? {
    //        return indexName
    //    }

       // call correct section when index is tapped
       func tableView(_ tableView: UITableView, sectionForSectionIndexTitle title: String, at index: Int) -> Int {

    guard let index = indexName.index(of: title) else {
        return -1
    }

    return index

}

      ///for showing next detailed screen with the downloaded info
     func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {

     let vc = self.storyboard?.instantiateViewController(withIdentifier: "DetailViewController") as! DetailViewController
    vc.imageString = imgURLArray[indexPath.section][indexPath.row]
    vc.nameString = nameArray[indexPath.section][indexPath.row]
    vc.dobString = dobArray[indexPath.section][indexPath.row]

    self.navigationController?.pushViewController(vc, animated: true)
}
}

Заранее большое спасибо!!! Диан.


person Dian    schedule 12.05.2017    source источник
comment
Какой у Вас вопрос? Какие у вас проблемы? И, пожалуйста, размещайте только соответствующий код.   -  person rmaddy    schedule 12.05.2017
comment
Я пытаюсь отобразить все разные рестораны (Марина-1, Марина-2...) в строках и название района (Марина) в разделе TableView. Спасибо.   -  person Dian    schedule 12.05.2017
comment
Почему приведенный выше JSON не соответствует ссылке на файл JSON?   -  person Tristan Beaton    schedule 12.05.2017
comment
Извините ребята. URL-адрес JSON сейчас правильный. Спасибо   -  person Dian    schedule 12.05.2017


Ответы (1)


Ваш JSON выше и JSON из этого URL отличаются, поэтому то, что у меня есть, может работать для обоих.

import UIKit

struct Actor {

    var children: String

    var country: String

    var description: String

    var dob: String

    var height: String

    var image: String

    var name: String

    var spouse: String

    init?(dict:Dictionary<String,String>) {

        guard

            let children = dict["children"],

            let country = dict["country"],

            let description = dict["description"],

            let dob = dict["dob"],

            let height = dict["height"],

            let image = dict["image"],

            let name = dict["name"],

            let spouse = dict["spouse"]

        else {

            return nil
        }

        self.children =  children

        self.country =  country

        self.description =  description

        self.dob =  dob

        self.height =  height

        self.image =  image

        self.name =  name

        self.spouse =  spouse
    }
}

struct Restaurant {

    var name: String

    var dob: String

    var image: String

    init?(dict:Dictionary<String,String>) {

        guard

            let name = dict["name"],

            let dob = dict["dob"],

            let image = dict["image"]

        else {

            return nil
        }

        self.name = name

        self.dob = dob

        self.image = image
    }
 }

struct NeighborhoodActors {

    var name: String

    var actors: Array<Actor>

    init(name:String, data:Array<Dictionary<String,String>>) {

        self.name = name

        self.actors = Array<Actor>()

        for dict in data {

            if let actor = Actor(dict: dict) {

                self.actors.append(actor)
            }
        }
    }
}

struct NeighborhoodRestaurants {

    var name: String

    var restaurants: Array<Restaurant>

    init(name:String, data:Array<Dictionary<String,String>>) {

        self.name = name

        self.restaurants = Array<Restaurant>()

        for dict in data {

            if let restaurant = Restaurant(dict: dict) {

                self.restaurants.append(restaurant)
            }
        }
    }
 }

class ViewController: UITableViewController {

    let urlString = "http://barhoppersf.com/json/hoods.json"

    var tableData = Array<NeighborhoodRestaurants>()

    override func viewDidLoad() {
        super.viewDidLoad()

        self.downloadJsonWithURL() // This loads tableview with data from url

        load(file: "document") // This loads tableview with the json in your question, which I put in a json file to test
    }

    func load(file:String) {

        guard let path = Bundle.main.path(forResource: file, ofType: "json") else { return }

        guard let data = try? Data(contentsOf: URL(fileURLWithPath: path)) else { return }

        guard let json = try? JSONSerialization.jsonObject(with: data) else { return }

        guard let dict = json as? Dictionary<String,Dictionary<String,Dictionary<String,Array<Dictionary<String,String>>>>> else { return }

        guard let hoods = dict["hoods"] else { return }

        guard let names = hoods["neighborhoodNames"] else { return }

        for (key, value) in names {

            let neighborhood = NeighborhoodRestaurants(name: key, data: value)

            self.tableData.append(neighborhood)
        }

        self.tableData.sort { $0.name > $1.name } // This will arrange the restaurants alphabetically

        self.tableView.reloadData()
    }

    func downloadJsonWithURL() {

        let url = NSURL(string: urlString)

        URLSession.shared.dataTask(with: (url as URL?)!, completionHandler: {(data, response, error) -> Void in

            if let error = error {

                print(error.localizedDescription)

                return
            }

            if let data = data {

                guard let json = try? JSONSerialization.jsonObject(with: data) else { return }

                guard let dict = json as? Dictionary<String,Dictionary<String,Dictionary<String,Array<Dictionary<String,String>>>>> else { return }

                guard let hoods = dict["hoods"] else { return }

                guard let names = hoods["neighborhoodNames"] else { return }

                for (key, value) in names {

                    let neighborhood = NeighborhoodActors(name: key, data: value)

                    // self.tableData.append(neighborhood)
                }

                DispatchQueue.main.async {

                    self.tableView.reloadData()
                }
            }

        }).resume()
    }

    // MARK: - changing the color, background and position of the header
    override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int)  -> UIView? {

        let headerView = UIView()

        headerView.backgroundColor = UIColor.self.init(red: 254/255, green:  170/255, blue: 25/255, alpha: 1.0)

        let headerLabel = UILabel(frame: CGRect(x: 8, y: 5, width: tableView.bounds.size.width, height: tableView.bounds.size.height))

        headerLabel.font = UIFont(name: "Trebuchet MS", size: 15)

        headerLabel.textColor = UIColor.darkGray

        headerLabel.text = self.tableView(self.tableView, titleForHeaderInSection: section)

        headerLabel.sizeToFit()

        headerView.addSubview(headerLabel)

        return headerView
    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

        return self.tableData[section].restaurants.count
    }

    override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {

        return self.tableData[section].name
    }

    override func numberOfSections(in tableView: UITableView) -> Int {

        return self.tableData.count
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)

        cell.textLabel?.text = self.tableData[indexPath.section].restaurants[indexPath.row].name

        return cell
    }

    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {

        let vc = self.storyboard?.instantiateViewController(withIdentifier: "DetailViewController") as! DetailViewController

        vc.restaurant = self.tableData[indexPath.section].restaurants[indexPath.row]

        self.navigationController?.pushViewController(vc, animated: true)
    }
}

class DetailViewController: UIViewController {

    @IBOutlet var imageView: UIImageView!

    @IBOutlet weak var nameLabel: UILabel!

    @IBOutlet weak var dobLabel: UILabel!

    var restaurant: Restaurant!

    override func viewDidLoad() {
        super.viewDidLoad()

        self.nameLabel.text = self.restaurant.name

        self.dobLabel.text = self.restaurant.dob

        if let url = URL(string: self.restaurant.image) {

            let task = URLSession.shared.dataTask(with: url) { data, resonse, error in

                if let error = error {

                    print(error.localizedDescription)

                    return
                }

                if let data = data {

                    let image = UIImage(data: data)

                    DispatchQueue.main.async {

                        self.imageView.image = image
                    }
                }
            }

            task.resume()
        }
    }
}

Вот как открыть URL-адрес, открыть карты Apple и позвонить по номеру телефона.

func telephone(phoneNumber:String) {

    let application = UIApplication.shared

    if application.openURL(URL(string: "tel://\(phoneNumber)")!) {

        print("Ringing")

    } else {

        print("Something has gone wrong.")
    }
}

func directions(address:String) {

    let geocoder = CLGeocoder()

    geocoder.geocodeAddressString(address) { (placemarks, error) in

        if let description = error?.localizedDescription {

            print(description)

            return
        }

        if let placemark = placemarks?.first {

            let pin = MKPlacemark(placemark: placemark)

            let item = MKMapItem(placemark: pin)

            let region = MKCoordinateRegionMakeWithDistance(pin.coordinate, 1000, 1000)

            let options: Dictionary<String,Any> = {

                var dict = Dictionary<String,Any>()

                dict[MKLaunchOptionsMapCenterKey] = NSValue(mkCoordinate: region.center)

                dict[MKLaunchOptionsMapSpanKey] = NSValue(mkCoordinateSpan: region.span)

                return dict
            }()

            item.openInMaps(launchOptions: options)

            return
        }

        print("An unknown error has occured.")
    }
}

func website(url:String) {

    if let url = URL(string: url) {

        let application = UIApplication.shared

        application.openURL(url)

        return
    }

    print("The URL is invalid.")
}
person Tristan Beaton    schedule 12.05.2017
comment
Тристан, спасибо за вашу помощь и ответ, но здесь это не работает. Может быть, я делаю что-то не так. Я получил сообщение об ошибке: «Завершение работы приложения из-за необработанного исключения« NSInternalInconsistencyException », причина:«-[UITableViewController loadView] загрузил перо BYZ-38-t0r-view-8bC-Xf-vdC, но не получил UITableView». Есть идеи? - person Dian; 12.05.2017
comment
В вашей раскадровке это UIViewController или UITableViewController? - person Tristan Beaton; 12.05.2017
comment
Это ViewController - person Dian; 12.05.2017
comment
Если вы добавите UITableViewController в свою раскадровку и установите класс, который я сделал выше, он должен работать. Если вам нужно использовать UIViewController, вам нужно добавить выход UITableView и установить UITableViewDelegate и UITableViewDatasource. Кроме того, измените суперкласс с UITableViewController на UIViewController и удалите переопределение из всех методов просмотра таблиц. - person Tristan Beaton; 12.05.2017
comment
У меня это сработало. Большое спасибо. Теперь мне нужно получить информационный дисплей, когда я нажимаю на ячейки (Марина-1, Марина-2...). Я настроил его с помощью TableViewCell и TableViewController. - person Dian; 12.05.2017
comment
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { let vc = self.storyboard?.instantiateViewController(withIdentifier: DetailViewController) as! DetailViewController vc.imageString = imgURLArray[indexPath.section][indexPath.row] vc.nameString = nameArray[indexPath.section][indexPath.row] vc.dobString = dobArray[indexPath.section][indexPath.row] self.navigationController? .pushViewController(vc, анимированный: true) } - person Dian; 12.05.2017
comment
Тристан, данные загружаются только из локального файла. Я не могу загрузить URL. Есть идеи? - person Dian; 12.05.2017
comment
Тристан, у меня работает URL. Еще раз спасибо мужик! Я ценю это. Мне просто нужно понять, как отображать всю информацию из URL-адреса json внутри DetailViewController, который у меня есть. Я использую: func tableView (_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { let vc = self.storyboard?.instantiateViewController (withIdentifier: DetailViewController) as! DetailViewController self.navigationController?.pushViewController(vc, анимированный: true) } Заранее СПАСИБО. - person Dian; 13.05.2017
comment
Я добавил некоторый код выше, чтобы иметь дело с выбором ячейки. - person Tristan Beaton; 13.05.2017
comment
Тристан. Извините за все вопросы. Я не могу отображать информацию, которая мне нравится, в DetailViewController. Я пытаюсь показать название ресторана (например, Марина-1), дату рождения (дата рождения) из json, а также изображение. Прямо сейчас у меня есть это в моем DetailViewController: - person Dian; 13.05.2017
comment
` @IBOutlet var imageView: UIImageView! @IBOutlet слабая переменная nameLabel: UILabel! @IBOutlet слабая переменная dobLabel: UILabel! переменная nameString:String! вар добСтрока:Строка! var imageString:Строка! переопределить функцию viewDidLoad() { super.viewDidLoad() print(restaurant) self.updateUI() } - person Dian; 13.05.2017
comment
остальное..... func updateUI () { self.nameLabel.text = nameString self.dobLabel.text = dobString let imgURL = URL (string: imageString) let data = NSData (contentsOf: (imgURL)!) self.imageView .image = UIImage(data: data! as Data) } Надеюсь, вы поможете!!! - person Dian; 13.05.2017
comment
Тристан, может быть, я могу отправить тебе текущий проект по электронной почте. может быть будет легче. Спасибо большое. Действительно очень ценю вашу помощь. - person Dian; 13.05.2017
comment
Я изменил DetailViewController выше, чтобы он работал с вашей функцией updateUI. - person Tristan Beaton; 13.05.2017
comment
Тристан, большое спасибо за помощь. Все работает отлично. Я действительно ценю твою помощь. Благодарю вас! Благодарю вас! - person Dian; 13.05.2017
comment
Тристан, извините за беспокойство, но по какой-то причине имена вытяжек из JSON-файла не отображаются в правильном порядке. Марина первая в файле, но отображается второй. Может быть, мне нужно отсортировать их по алфавиту, который мне действительно нужен. Есть идеи?? Спасибо чувак. - person Dian; 14.05.2017
comment
Вы хотите, чтобы они были в том порядке, в котором они находятся в файле JSON? - person Tristan Beaton; 14.05.2017
comment
они будут в алфавитном порядке в конечном итоге. поэтому я могу перечислить их в алфавитном порядке в файле JSON или, если есть функция для их сортировки в алфавитном порядке, будет работать. Спасибо чувак. - person Dian; 14.05.2017
comment
Есть функция сортировки по алфавиту. Я просто тестирую это. - person Tristan Beaton; 14.05.2017
comment
Этот код здесь отсортирует их в алфавитном порядке self.tableData.sort { $0.name > $1.name }. Я также добавил это к ответу выше. Это перед tableView.reloadData() в функции loadFile(). - person Tristan Beaton; 14.05.2017
comment
Оно работало завораживающе. В очередной раз благодарим за помощь. Я действительно новичок в этом, поэтому любая помощь приветствуется. Привет из Сан-Франциско!! - person Dian; 14.05.2017
comment
Как вы думаете, я могу отобразить URL-адрес в Safari из кода, с которым вы мне помогли? Прямо сейчас он отображает метку, но мне нужно сделать ее кнопкой и проанализировать Json, чтобы я мог щелкнуть каждый URL-адрес, полученный из Json. - person Dian; 23.05.2017
comment
Я не очень понимаю ваш вопрос. Вы хотите, чтобы кнопка открывала URL-адрес в сафари? - person Tristan Beaton; 23.05.2017
comment
Извините, если я не был ясен. Да. Я хочу, чтобы кнопка открывала URL. Что я делаю, так это отображаю информацию о ресторанах в контроллере подробного представления из файла json. У каждого ресторана отображается название., кухня.. Я должен сделать сайт, местоположение (адрес) и номер телефона активными (кликабельными). Прямо сейчас у меня есть ярлыки, которые просто отображают название веб-сайта, но не кликабельны. Надеюсь, я имею смысл. Спасибо дружище. - person Dian; 23.05.2017
comment
Используйте в DidLoad: - person Dian; 23.05.2017
comment
.......... func tapFunction(sender:UITapGestureRecognizer) { UIApplication.shared.open(URL(string: self.restaurant.website)!, options: [:], completeHandler: nil) } - person Dian; 23.05.2017
comment
Мне просто нужно выяснить, как позвонить по номеру, нажав на номер, и открыть карту Apple, нажав на адрес. Любые идеи? - person Dian; 23.05.2017
comment
Вы можете использовать UIApplication.shared.open() как для телефонных номеров, так и для карт. Это для телефонных номеров. Это для карт - person Tristan Beaton; 23.05.2017
comment
Я пробую эту функцию, но мне не повезло: func phone(phoneLabel: String) { if let url = URL(string: self.restaurant.phone) { if # available(iOS 10, *) { UIApplication.shared.open(url, варианты: [:], completeHandler: nil) } else { UIApplication.shared.openURL (url как URL) } } } - person Dian; 23.05.2017
comment
Интересно, нужно ли ставить phoneLabel.isUserInteractionEnabled = true - person Dian; 23.05.2017
comment
также проблема в том, что ссылка, которую вы отправили, использует кнопку, а я использую ярлык. В очередной раз благодарим за помощь. Действительно ценится. - person Dian; 23.05.2017
comment
Обязательно ли использовать ярлыки? Потому что я бы поменял их на пуговицы. Если вы собираетесь использовать ярлыки пользователей, используйте жест касания для вызова функций, которые я добавил в свой ответ. - person Tristan Beaton; 24.05.2017
comment
Я проверю это позже. Как вы думаете, это будет работать с текущими ярлыками, которые у меня есть? Большое спасибо, приятель. - person Dian; 24.05.2017
comment
Можете ли вы добавить скриншот того, как выглядит ваш контроллер представления? Исходя из этого, я смогу сказать, будут ли работать ваши лейблы. - person Tristan Beaton; 24.05.2017
comment
Я заставил телефон работать. Я использовал кнопку. Теперь осталось разобраться, как открыть адрес в картах. Спасибо. Я использую этот оператор: self.addressButton.setTitle(self.restaurant.address,для:.normal) self.phoneButton.setTitle(self.restaurant.phone,для:.normal) - person Dian; 24.05.2017
comment
Я просто использовал и работал: @IBAction func addressButton(_ sender: Any) { let baseUrl: String = maps. apple.com/?q= let encodedName = restaurant.address.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)! let finalUrl = baseUrl + encodedName, если let url = URL(string: finalUrl) { UIApplication.shared.open(url, options: [:], completeHandler: nil) } } - person Dian; 24.05.2017
comment
Еще раз спасибо за ваш вклад. Действительно ценю это. - person Dian; 26.05.2017
comment
Надеюсь у тебя все хорошо. посмотрите, можете ли вы помочь мне с этим вопросом: Спасибо, приятель. stackoverflow.com/questions/44877074/ - person Dian; 03.07.2017