【问题标题】:JSON data do not load in UICollectionView Swift 5JSON 数据不会在 UICollectionView Swift 5 中加载
【发布时间】:2021-09-26 08:56:36
【问题描述】:

目前我正在尝试从 News API 加载数据,在此之前我曾尝试使用 UITableView 实现它并且它可以工作,但它在 UICollectionView 中不起作用。我正在使用 URLSession 进行网络操作。

PS; 我使用 Storyborad 中对象库中的预制 collectionView 完成此操作

我期望发生的事情

  • JSON 数据加载到 CollectionView 中

实际发生的情况

  • CollectionView 中未加载 JSON,屏幕为空

我尝试解决的问题

  • 在附加 JSON 后重新加载 collectionView

NewsCollectionVC.swift

class NewsCollectionVC: UIViewController {

    var newsResult = [NewsModel]()
    var newsManager = NewsManager()
    @IBOutlet weak var collectionView: UICollectionView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        collectionView.delegate = self
        collectionView.dataSource = self
        newsManager.fetchNews()
        newsManager.delegate = self
        collectionView.reloadData()
    }
    
}

extension NewsCollectionVC : NewsManagerDelegate {
    func didSendNewsData(_ newsManager: NewsManager, with news: [NewsModel]) {
        
        self.newsResult.append(contentsOf: news)
        DispatchQueue.main.async{

            self.collectionView.reloadData()
            print(self.newsResult.count)
        }
    }
}

extension NewsCollectionVC : UICollectionViewDataSource{
    

    // Tell delegate how many cv need to show
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        

        return newsResult.count
    }
    
    // Tell delegate content of cv
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        
        let listOfNews = newsResult[indexPath.row]
        
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! CViewCell
        cell.author.text = listOfNews.author
        //cell.author.text = "listOfNews.author"
        
        return cell
    }
}

NewsManager.swift

protocol NewsManagerDelegate {
    func didSendNewsData (_ newsManager: NewsManager, with news : [NewsModel])
}


struct NewsManager{
    
    var delegate : NewsManagerDelegate?
    
    let newsUrl = "https://newsapi.org/v2/everything?q=apple"
    let key = "MY_API_KEY"
    
    func fetchNews(){
        let urlString = "\(newsUrl)&apiKey=\(key)"
        performRequest(with : urlString)
    }
    
    func fetchNews(page : Int){
        let urlString = "\(newsUrl)&page=\(page)&apiKey=\(key)"
        performRequest(with: urlString)
    }

    
    func performRequest(with urlString : String){
        
        
        // 1. create URL object
        guard let url = URL(string: urlString) else {fatalError("There's problem to fetch data from this url")}
        
        // 2. create session, object to do networking
        let session = URLSession(configuration: .default)
        
        //3. give session a task
        let task = session.dataTask(with: url) { (data, response, error) in
            
            if error != nil{
                print("Error in giving session a task \(String(describing: error?.localizedDescription))")
                
            } else{
                // determine if data is exist
                if let safeData = data {
                    //convert data to string
                    //let dataString = String(data: safeData, encoding: .utf8)
                    //print(dataString)
                    
                    // Parse the data here
                    guard let news = parseJSON(safeData) else{ fatalError("Error parsing data from JSON")}
                   
                    DispatchQueue.main.async {
                        delegate?.didSendNewsData(self, with: news)
                    }
                    
                }
            }
        }
        //start the task
        task.resume()
    }
    
    func parseJSON(_ newsData : Data) -> [NewsModel]?{
        
        
        let decoder = JSONDecoder()
    
        var newsModel = [NewsModel]()
        
        do{
            
            let decodeData = try decoder.decode(NewsData.self, from: newsData)
            print(decodeData.articles[0].title)
            print(decodeData.articles[0].source.name)
            print(decodeData.articles[0].urlToImage)
            
            
          
            
            for article in decodeData.articles {
                
                let author = article.author
                let title = article.title
                let decription = article.description
                let url = article.url
                let urlToImage = article.urlToImage
                let publishedAt = article.publishedAt
                let sourcesName = article.source.name
                
                
                    guard let imageData = try? Data(contentsOf: urlToImage) else{fatalError("Error to get image data from URL")}
                     guard let imageContent = UIImage(data: imageData) else {fatalError("Error load image from image data")}
                    let data = NewsModel(author: author, title: title, decription: decription, url: url, image: imageContent, publishedAt: publishedAt, sourcesName: sourcesName)
                    
                        newsModel.append(data)

            }
            
            return newsModel
            
        } catch{
            print("Error decode the data : \(error.localizedDescription)")
    
        }

        return newsModel
    }
}

NewsModel.swift

struct NewsModel {
    let author : String
    let title : String
    let decription : String
    let url : String
    let image : UIImage
    let publishedAt : String
    let sourcesName : String
    
}

【问题讨论】:

  • 你有没有试过在添加断点后运行你的代码,看看它的哪一部分失败了?
  • @Abizern ya 我已经在断点处,似乎 numberOfItemsInSection 为 0,我不知道为什么,当我在 tableview 尝试它时它返回 20
  • @EricAya 如您所见,我解码 JSON 并将其解析回数据模型,单个类中可能有多个 URLSession ????,我打算使用它您建议的一种方法,但我找不到如何做到这一点。
  • 您是否在您的委托方法中放置了一个断点以查看 何时 它正在运行?
  • @Abizern 我只是尝试在 newsManagerDelegate 和采用它的类中添加断点,它都返回成功而不是 nil

标签: ios swift uicollectionview


【解决方案1】:

感谢您花时间回答这些问题,答案是因为我在调用实例方法后为委托分配了实例。

应该是✅

override func viewDidLoad() {
        super.viewDidLoad()
        collectionView.delegate = self
        collectionView.dataSource = self

        newsManager.delegate = self
        newsManager.fetchNews()
        
        collectionView.reloadData()
    }

而不是❎

override func viewDidLoad() {
        super.viewDidLoad()
        collectionView.delegate = self
        collectionView.dataSource = self
        newsManager.fetchNews()
        newsManager.delegate = self
        collectionView.reloadData()
    }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-04-12
    • 1970-01-01
    • 2012-03-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多