Home > Back-end >  Unable to display the data into UICollection view
Unable to display the data into UICollection view

Time:05-20

I am trying to display the data into UI collection view .I am following the MVVM Architecture pattern. I am following programatic approach . I put the break point , I can see onto console , The data is decoded from URL but problem is it not displaying the into view controller.

Here is the my network manager code .

import Foundation

protocol NetworkManagerProtocol{
    func getModel<T: Codable>(_ model: T.Type, from url: String, completion: @escaping (Result<T, NetworkError>) -> Void)
    func getData(from url: String, completion: @escaping (Result<Data, NetworkError>) -> Void)
  
}
class NetworkManager:  NetworkManagerProtocol{
    
    func getModel<T: Codable>(_ model: T.Type, from url: String, completion: @escaping (Result<T, NetworkError>) -> Void) {
        
        guard let url = URL(string: url) else {
            completion(.failure(.badURL))
            return
        }
        
        URLSession.shared.dataTask(with: url) { data, response, error in
            if let data = data {
                do {
                    let response = try JSONDecoder().decode(model, from: data)
                    completion(.success(response))
                } catch let error {
                    completion(.failure(.other(error)))
                }
            }
            
            if let error = error {
                completion(.failure(.other(error)))
            }
        }
        .resume()
        
    }
    
    func getData(from url: String, completion: @escaping (Result<Data, NetworkError>) -> Void) {
        guard let url = URL(string: url) else {
            completion(.failure(.badURL))
            return
        }
        
        URLSession.shared.dataTask(with: url) { data, response, error in
            if let data = data {
                completion(.success(data))
            }
            
            if let error = error {
                completion(.failure(.other(error)))
            }
        }
        .resume()
    }
}

Here is the View Model code.

import Foundation

class PhotoViewModel {
    
    
   private let networkManager: NetworkManagerProtocol
    
   
    var  hits = [Hit]()
    private var cache = [String: Data]()
    
    private weak var delegate: PhotoViewable?
    
    
    init(networkManager: NetworkManagerProtocol) {
        self.networkManager = networkManager
       
    }
    
    var rows: Int {
        return hits.count
    }
    
    func fecthPhotoRecord(){
        
        let networkUrl = NetworkURLs.baseURL
        networkManager.getModel(Photo.self, from: networkUrl) { [weak self] result in
            switch result{
            case.success(let photo):
                self?.hits = photo.hits
                self?.delegate?.refreshUI()
            case.failure(let error):
                print(error)
               self?.delegate?.showError()
            }
        
        }
    }
    func downloadImage(row: Int, completion: @escaping (Data) -> Void) {
        
        let hit = hits[row]
        let hitpath = hit.previewURL
        
        if let data = cache[hitpath] {
            completion(data)
            return
        }
        
        networkManager
            .getData(from: hitpath) { result in
                switch result {
                case .success(let data):
                    self.cache[hitpath] = data
                    DispatchQueue.main.async {
                        completion(data)
                    }
                case .failure(let error):
                    print(error)
                }
            }
    }
    
    
}

Here is the collection view cell ..

import UIKit

protocol ReusableView: AnyObject {
    static var identifier: String { get }
}
class PhotoCollectionViewCell: UICollectionViewCell {
    
    private lazy var imageView: UIImageView = {
        let imageView = UIImageView()
        imageView.frame = CGRect(x:0, y:0, width:50, height:50)
        imageView.translatesAutoresizingMaskIntoConstraints = false
        return imageView
    }()
    
   private lazy  var tagLabel:UILabel = {
        let label = UILabel()
        label.font = UIFont.boldSystemFont(ofSize: 15)
        label.textColor = .black
        label.translatesAutoresizingMaskIntoConstraints = false
        return label
    }()
    
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        
        imageView.translatesAutoresizingMaskIntoConstraints = false
        self.contentView.addSubview(tagLabel)
            self.contentView.addSubview(imageView)
            NSLayoutConstraint.activate([
                
                tagLabel.topAnchor.constraint(equalTo: self.contentView.topAnchor),
                tagLabel.bottomAnchor.constraint(equalTo: self.contentView.bottomAnchor),
                tagLabel.leadingAnchor.constraint(equalTo: self.contentView.leadingAnchor),
                tagLabel.trailingAnchor.constraint(equalTo: self.contentView.trailingAnchor),
        
                imageView.topAnchor.constraint(equalTo: self.tagLabel.topAnchor),
                imageView.leadingAnchor.constraint(equalTo: self.contentView.leadingAnchor),
                imageView.trailingAnchor.constraint(equalTo: self.contentView.trailingAnchor),
            ])
           
            self.contentView.backgroundColor = .lightGray
    }
    func configureCell(tagName: String) {
        tagLabel.text = tagName
      
        
        }
    func configureImageCell(row: Int, viewModel: PhotoViewModel) {
        
        imageView.image = nil
        
        viewModel
            .downloadImage(row: row) { [weak self] data in
                let image = UIImage(data: data)
                self?.imageView.image = image
            }
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    
}
extension PhotoCollectionViewCell: ReusableView {
    static var identifier: String {
        return String(describing: self)
    }
}

Here is the code for view controller .

import UIKit

protocol PhotoViewable: AnyObject {
    func refreshUI()
    func showError()
}

class PhotoViewController: UIViewController {

    var viewModel : PhotoViewModel
    
    init(viewModel : PhotoViewModel) {
        self.viewModel = viewModel
        super.init(nibName: nil, bundle: nil)
    }
    
    private let collectionView: UICollectionView = {
           let viewLayout = UICollectionViewFlowLayout()
           let collectionView = UICollectionView(frame: .zero, collectionViewLayout: viewLayout)
           collectionView.backgroundColor = .white
           return collectionView
       }()
    
    private enum LayoutConstant {
           static let spacing: CGFloat = 16.0
           static let itemHeight: CGFloat = 300.0
       }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    private var activityIndicator: UIActivityIndicatorView = {
       let activityIndicator = UIActivityIndicatorView()
       activityIndicator.translatesAutoresizingMaskIntoConstraints = false
       return activityIndicator
   }()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        setupViews()
        setupLayouts()
        viewModel.fecthPhotoRecord()
        
        collectionView.delegate = self
        collectionView.dataSource = self
        view.backgroundColor = .lightGray
        collectionView.reloadData()
      

    }
    
    private func setupViews() {
            view.backgroundColor = .white
            view.addSubview(collectionView)

            collectionView.dataSource = self
            collectionView.delegate = self
            collectionView.register(PhotoCollectionViewCell.self, forCellWithReuseIdentifier: PhotoCollectionViewCell.identifier)
        }
    private func setupLayouts() {
           collectionView.translatesAutoresizingMaskIntoConstraints = false

           // Layout constraints for `collectionView`
           NSLayoutConstraint.activate([
               collectionView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
               collectionView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor),
               collectionView.leftAnchor.constraint(equalTo: view.leftAnchor),
               collectionView.rightAnchor.constraint(equalTo: view.rightAnchor)
           ])
       }
    
}

extension PhotoViewController: UICollectionViewDataSource {
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return viewModel.hits.count
    }

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: PhotoCollectionViewCell.identifier, for: indexPath) as! PhotoCollectionViewCell
        
        let row = indexPath.row
        let hits  = viewModel.hits[indexPath.row]
        cell.configureCell(tagName: hits.tags)
        cell.configureImageCell(row: row, viewModel: viewModel)
        
        return cell
    }
}

extension PhotoViewController: UICollectionViewDelegateFlowLayout {
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {

        let width = itemWidth(for: view.frame.width, spacing: LayoutConstant.spacing)

        return CGSize(width: width, height: LayoutConstant.itemHeight)
    }

    func itemWidth(for width: CGFloat, spacing: CGFloat) -> CGFloat {
        let itemsInRow: CGFloat = 2

        let totalSpacing: CGFloat = 2 * spacing   (itemsInRow - 1) * spacing
        let finalWidth = (width - totalSpacing) / itemsInRow

        return floor(finalWidth)
    }

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
        return UIEdgeInsets(top: LayoutConstant.spacing, left: LayoutConstant.spacing, bottom: LayoutConstant.spacing, right: LayoutConstant.spacing)
    }

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
        return LayoutConstant.spacing
    }

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
        return LayoutConstant.spacing
    }
}

extension PhotoViewController: PhotoViewable {
    func refreshUI() {
        DispatchQueue.main.async {
           self.collectionView.isHidden = false
           // self.activityIndicator.stopAnimating()
            self.collectionView.reloadData()
        }
    }
    
    func showError() {
        DispatchQueue.main.async {
           // self.activityIndicator.stopAnimating()
            self.collectionView.reloadData()
        }
    }
}

Here is the console .

enter image description here

Here is the result of the screenshot. enter image description here

CodePudding user response:

The problem is you are not setting the delegate for viewModel add the following line in viewDidLoad before calling viewModel.fecthPhotoRecord()

viewModel.delegate = self

CodePudding user response:

main problem is you didn't call the delegate for your viewModel before fetching the data, always setup you delegate before any data management in viewDidLoad

viewModel.delegate = self
viewModel.datasource = self //if needed datasource
  • Related