表格视图中未加载JSON

时间:2018-07-05 07:03:37

标签: ios json swift uitableview

这是我的具有表视图的视图控制器

class HomeVC: UIViewController, UITableViewDelegate, UITableViewDataSource {    

    private let myArray: NSArray = ["First", "Second", "Third"]
    private var myTableView: UITableView!

    var articles: [ArticleClass]? = []


    override func viewDidLoad() {
        super.viewDidLoad()

        view.backgroundColor = UIColor.white
        navigationController?.navigationBar.prefersLargeTitles = true
        navigationItem.title = "Home"

        getData()

        let barHeight: CGFloat = UIApplication.shared.statusBarFrame.size.height
        let displayWidth: CGFloat = self.view.frame.width
        let displayHeight: CGFloat = self.view.frame.height

        myTableView = UITableView(frame: CGRect(x: 0, y: barHeight, width: displayWidth, height: displayHeight - barHeight))
        myTableView.dataSource = self
        myTableView.delegate = self
        myTableView.register(ArticleCell.self, forCellReuseIdentifier: "MyCell")

        view.addSubview(myTableView)
        myTableView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
        myTableView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
        myTableView.topAnchor.constraint(equalTo: view.topAnchor, constant: 20).isActive = true
        myTableView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true

    }


    func getData() {

        let theURL = URL(string: "https://newsapi.org/v2/top-headlines?country=us&category=business&apiKey=34e81be767734526b224ac353b1378e8")
        let task = URLSession.shared.dataTask(with: theURL!) { (data, response, error) in

            if error != nil {

                print(error)
                return

            } else {

                self.articles = [ArticleClass]()

                do {

                    let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as! [String: AnyObject]

                    if let articlesFromJSON = json["Articles"] as? [[String: Any]] {

                        for articleOutOfJSON in articlesFromJSON {

                            let theArticle = ArticleClass()

                            if let title = articleOutOfJSON as? String, let author = articleOutOfJSON["author"] as? String, let desc = articleOutOfJSON["description"] as? String, let url = articleOutOfJSON["url"] as? String, let imageToURL = articleOutOfJSON["imageToURL"] as? String {

                                theArticle.theDescription = desc
                                theArticle.author = author
                                theArticle.imageURL = imageToURL
                                theArticle.url = url

                            }

                            //Putting the articleOutOfJSON into our array.
                            self.articles?.append(theArticle)

                        }

                    }

                    //Making the data be on the main thread.
                    DispatchQueue.main.async {

                        self.myTableView.reloadData()

                    }


                } catch {

                    print(error)

                }

            }

        }

        task.resume()

    }




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

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

        let cell = tableView.dequeueReusableCell(withIdentifier: "MyCell", for: indexPath) as! ArticleCell

        cell.title.text = self.articles?[indexPath.item].headline
        cell.theDesc.text = self.articles?[indexPath.item].theDescription
        cell.author.text = self.articles?[indexPath.item].author
        cell.theImageView.downloadImage(from: (self.articles?[indexPath.item].url)!)

        return cell

    }

    func numberOfSections(in tableView: UITableView) -> Int {
        return self.articles?.count ?? 0
    }


}

这是我的表格视图单元格。

class ArticleCell: UITableViewCell {

    let title: UILabel = {
        let label = UILabel()
        label.text = "Title"
        label.translatesAutoresizingMaskIntoConstraints = false
        return label
    }()

    let theDesc: UILabel = {
        let label = UILabel()
        label.text = "TEXT TEXT TEXT TEXT TEXT TEXT"
        label.translatesAutoresizingMaskIntoConstraints = false
        return label
    }()

    let author: UILabel = {
        let label = UILabel()
        label.text = "Author"
        label.translatesAutoresizingMaskIntoConstraints = false
        return label
    }()

    let theImageView: UIImageView = {
        let image = UIImageView()
        image.backgroundColor = UIColor.purple
        image.translatesAutoresizingMaskIntoConstraints = false
        return image
    }()

    override func awakeFromNib() {
        super.awakeFromNib()

        contentView.addSubview(theImageView)
        theImageView.leftAnchor.constraint(equalTo: contentView.leftAnchor, constant: 8).isActive = true
        theImageView.rightAnchor.constraint(equalTo: contentView.rightAnchor, constant: -146).isActive = true
        theImageView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 30).isActive = true
        theImageView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -26).isActive = true

        contentView.addSubview(title)
        contentView.addSubview(theDesc)
        contentView.addSubview(author)

    }

    override func setSelected(_ selected: Bool, animated: Bool) {
        super.setSelected(selected, animated: animated)

        // Configure the view for the selected state
    }

}

这是我的数据模型。

class ArticleClass: NSObject {
    var headline: String?
    var theDescription: String?
    var author: String?
    var url: String?
    var imageURL: String?
    var publishingDate: Int?
}

我尝试将JSON加载到表视图中,但无法正常工作。我目前看不到代码有什么问题。如果可以的话,我将不胜感激。我也一直在网上寻求帮助,但由于遇到的问题而无法获得任何帮助。

3 个答案:

答案 0 :(得分:3)

我建议使用Decodable协议来解析JSON。

首先,您不需要从NSObject继承的类,一个结构就足够了。本文结构使用JSON键作为属性:

struct News : Decodable {
    let status : String
    let articles : [Article]
}

struct Article : Decodable {
    let description: String?
    let author: String?
    let url: URL
    let urlToImage: URL?
    let publishedAt: Date
}

然后将数据源数组声明为非可选

var articles = [Article]()

然后解析JSON

func getData() {
    let theURL = URL(string: "https://newsapi.org/v2/top-headlines?country=us&category=business&apiKey=••••••••••••••••••••")
    let task = URLSession.shared.dataTask(with: theURL!) { (data, response, error) in

        if error != nil { 
           print(error!)
           return 
        } else {
           do {
                let decoder = JSONDecoder()
                decoder.dateDecodingStrategy = .iso8601
                let news = try decoder.decode(News.self, from: data!)
                self.articles = news.articles

                //Making the data be on the main thread.
                DispatchQueue.main.async {
                    self.myTableView.reloadData()
                }
            } catch {
                print(error)
            }         
        }
    }
    task.resume()
}

您的代码中还有其他问题

您正在混合numberOfSectionsnumberOfRowsInSection。在numberOfSections中返回1或忽略方法

func numberOfSections(in tableView: UITableView) -> Int { return 1 }

numberOfRowsInSection中返回文章数

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return articles.count
}

cellForRow中使用.row而不是.item

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

    let cell = tableView.dequeueReusableCell(withIdentifier: "MyCell", for: indexPath) as! ArticleCell
    let article = self.articles[indexPath.row]
    cell.title.text = article.title
    cell.theDesc.text = article.description
    cell.author.text = article.author
    cell.theImageView.downloadImage(from: article.url)
    return cell
}

PS:

强烈建议您不要在公共论坛上共享真实的API密钥。更好的方法是发布JSON并使密钥乱码

答案 1 :(得分:2)

此行if let articlesFromJSON = json["Articles"] as? [[String: Any]] { 应该是

if let articlesFromJSON = json["articles"] as? [[String: Any]] {

答案 2 :(得分:2)

根据Husyn的回答,您需要替换

if let articlesFromJSON = json["Articles"] as? [[String: Any]] {

if let articlesFromJSON = json["articles"] as? [[String: Any]] {

因为您的Articles密钥与来自服务器的数据不匹配,还有另外一个与密钥不匹配的密钥是imageToURL,根据您的需要,该密钥需要替换为urlToImage来自服务器的数据以及您的最终代码将是:

let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as! [String: AnyObject]

                if let articlesFromJSON = json["articles"] as? [[String: Any]] {
                    for articleOutOfJSON in articlesFromJSON {

                        let theArticle = ArticleClass()

                        if let title = articleOutOfJSON["title"] as? String, let author = articleOutOfJSON["author"] as? String, let desc = articleOutOfJSON["description"] as? String, let url = articleOutOfJSON["url"] as? String, let imageToURL = articleOutOfJSON["urlToImage"] as? String {

                            theArticle.theDescription = desc
                            theArticle.author = author
                            theArticle.imageURL = imageToURL
                            theArticle.url = url

                        }
                        self.articles?.append(theArticle)
                    }

                }

如果服务器将发送null值,则整个对象将不会追加到您的articles对象中。因此,为了更好的方法,请检查以下代码:

let theArticle = ArticleClass()
theArticle.theDescription = articleOutOfJSON["description"] as? String ?? ""
theArticle.author = articleOutOfJSON["author"] as? String ?? ""
theArticle.imageURL = articleOutOfJSON["urlToImage"] as? String ?? ""
theArticle.url = articleOutOfJSON["url"] as? String ?? ""
self.articles?.append(theArticle)

numberOfRowsInSection应该是:

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return self.articles?.count ?? 0
}

然后删除:

func numberOfSections(in tableView: UITableView) -> Int {
    return self.articles?.count ?? 0
}