I am new to swift . I am trying to send the data from collection view to another view controller when user click the collection view cell. But the problem is when I click the cell it not going to next view controller with label and image properties instated it showing blank screen.
Here is the 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 .
import Foundation
class PhotoViewModel {
private let networkManager: NetworkManagerProtocol
var hits = [Hit]()
private var cache = [String: Data]()
// var net = NetworkManager()
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 view controller with collection cell ..
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 = 230.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
viewModel.delegate = 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
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
collectionView.deselectItem(at: indexPath, animated: false)
let detailVC = PhtotoDetailsViewController()
detailVC.rowSelected = indexPath.row
let navController = UINavigationController(rootViewController: detailVC)
navController.modalPresentationStyle = .fullScreen
present(navController, animated: true, completion: nil)
}
}
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
}
}
Here is the collection view cell .
protocol ReusableView: AnyObject {
static var identifier: String { get }
}
class PhotoCollectionViewCell: UICollectionViewCell {
private enum Constants {
// MARK: contentView layout constants
static let contentViewCornerRadius: CGFloat = 4.0
// MARK: profileImageView layout constants
static let imageHeight: CGFloat = 180.0
// MARK: Generic layout constants
static let verticalSpacing: CGFloat = 8.0
static let horizontalPadding: CGFloat = 16.0
static let profileDescriptionVerticalPadding: CGFloat = 8.0
}
private let imageView: UIImageView = {
let imageView = UIImageView(frame: .zero)
imageView.contentMode = .scaleAspectFill
return imageView
}()
private let tagLabel: UILabel = {
let label = UILabel(frame: .zero)
label.textAlignment = .center
label.numberOfLines = 0
return label
}()
override init(frame: CGRect) {
super.init(frame: .zero)
setupViews()
setupLayouts()
}
private func setupViews() {
contentView.clipsToBounds = true
contentView.layer.cornerRadius = Constants.contentViewCornerRadius
contentView.backgroundColor = .white
contentView.addSubview(imageView)
contentView.addSubview(tagLabel)
}
private func setupLayouts() {
imageView.translatesAutoresizingMaskIntoConstraints = false
tagLabel.translatesAutoresizingMaskIntoConstraints = false
// Layout constraints for `imageView`
NSLayoutConstraint.activate([
imageView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
imageView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor),
imageView.topAnchor.constraint(equalTo: contentView.topAnchor),
imageView.heightAnchor.constraint(equalToConstant: Constants.imageHeight)
])
// Layout constraints for `tagLabel`
NSLayoutConstraint.activate([
tagLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: Constants.horizontalPadding),
tagLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -Constants.horizontalPadding),
tagLabel.topAnchor.constraint(equalTo: imageView.bottomAnchor, constant: Constants.profileDescriptionVerticalPadding)
])
}
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 details view controller .
import UIKit
class PhtotoDetailsViewController: UIViewController {
var viewModel : PhotoDetailsViewModel?
var photoviewModel : PhotoViewModel?
private let imageView: UIImageView = {
let imageView = UIImageView(frame: .zero)
imageView.contentMode = .scaleAspectFill
imageView.translatesAutoresizingMaskIntoConstraints = false
return imageView
}()
private let tagLabel: UILabel = {
let label = UILabel(frame: .zero)
label.textAlignment = .center
label.numberOfLines = 0
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(tagLabel)
view.addSubview(imageView)
// setUpUI()
setPhoto()
}
private func setUpUI(){
tagLabel.text = viewModel?.hit.tags
}
var rowSelected = 0
private func setPhoto(){
photoviewModel?.downloadImage(row: rowSelected) { [weak self] data in
DispatchQueue.main.async {
let image = UIImage(data: data)
self?.imageView.image = image
}
}
}
}
Here is the result when I click the cell .
CodePudding user response:
in your func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath)
add the following line
detailVC.photoviewModel = viewModel
CodePudding user response:
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
collectionView.deselectItem(at: indexPath, animated: false)
let detailVC = PhtotoDetailsViewController()
detailVC.rowSelected = indexPath.row
let navController = UINavigationController(rootViewController: detailVC)
navController.modalPresentationStyle = .fullScreen
present(navController, animated: true, completion: nil)
}
You haven't set any viewmodel for PhotoDetailsViewController