I am trying to send the data in including image from one view controller to another . The data is fetching from API . The data is successfully loaded into first view controller including image but when I try to reuse same code with didSelectRow function I am getting following errors . Cannot assign value of type 'Data?' to type 'UIImage' . The error on this line dc.imagemovie = presenter.getImageData(by: row). Here is the code to fetch the data from API.
class MoviePresenter: MoviePresenterProtocol {
private let view: MovieViewProtocol
private let networkManager: NetworkManager
private var movies = [Movie]()
private var cache = [Int: Data]()
var rows: Int {
return movies.count
}
init(view: MovieViewProtocol, networkManager: NetworkManager = NetworkManager()) {
self.view = view
self.networkManager = networkManager
}
func getMovies() {
let url = "https://api.themoviedb.org/3/movie/popular?language=en-US&page=3&api_key=6622998c4ceac172a976a1136b204df4"
networkManager.getMovies(from: url) { [weak self] result in
switch result {
case .success(let response):
self?.movies = response.results
self?.downloadImages()
DispatchQueue.main.async {
self?.view.resfreshTableView()
}
case .failure(let error):
DispatchQueue.main.async {
self?.view.displayError(error.localizedDescription)
}
}
}
}
func getTitle(by row: Int) -> String? {
return movies[row].originalTitle
}
func getOverview(by row: Int) -> String? {
return movies[row].overview
}
func getImageData(by row: Int) -> Data? {
return cache[row]
}
private func downloadImages() {
let baseImageURL = "https://image.tmdb.org/t/p/w500"
let posterArray = movies.map { "\(baseImageURL)\($0.posterPath)" }
let group = DispatchGroup()
group.enter()
for (index, url) in posterArray.enumerated() {
networkManager.getImageData(from: url) { [weak self] data in
if let data = data {
self?.cache[index] = data
}
}
}
group.leave()
group.notify(queue: .main) { [weak self] in
self?.view.resfreshTableView()
}
}
}
Here is the code in view controller to display the data into table view cell .
class MovieViewController: UIViewController {
@IBOutlet weak var userName: UILabel!
@IBOutlet weak var tableView: UITableView!
private var presenter: MoviePresenter!
var finalname = ""
override func viewDidLoad() {
super.viewDidLoad()
userName.text = "Hello: " finalname
setUpUI()
// configure presenter
presenter = MoviePresenter(view: self)
presenter.getMovies()
}
private func setUpUI() {
tableView.dataSource = self
tableView.delegate = self
}
@IBAction func selectSegment(_ sender: UISegmentedControl) {
if sender.selectedSegmentIndex == 0{
setUpUI()
presenter = MoviePresenter(view: self)
presenter.getMovies()
}
}
}
extension MovieViewController: MovieViewProtocol {
func resfreshTableView() {
tableView.reloadData()
}
func displayError(_ message: String) {
let alert = UIAlertController(title: "Error", message: message, preferredStyle: .alert)
let doneButton = UIAlertAction(title: "Done", style: .default, handler: nil)
alert.addAction(doneButton)
present(alert, animated: true, completion: nil)
}
}
extension MovieViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
presenter.rows
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: MovieViewCell.identifier, for: indexPath) as! MovieViewCell
let row = indexPath.row
let title = presenter.getTitle(by: row)
let overview = presenter.getOverview(by: row)
let data = presenter.getImageData(by: row)
cell.configureCell(title: title, overview: overview, data: data)
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let dc = storyboard?.instantiateViewController(withIdentifier: "MovieDeatilsViewController") as! MovieDeatilsViewController
let row = indexPath.row
dc.titlemovie = presenter.getTitle(by: row) ?? ""
dc.overview = presenter.getOverview(by: row) ?? ""
**dc.imagemovie = presenter.getImageData(by: row)**
self.navigationController?.pushViewController(dc, animated: true)
}
}
extension MovieViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableView.automaticDimension
}
}
Here is the code for display the data .
class MovieDeatilsViewController: UIViewController {
@IBOutlet weak var movieImage: UIImageView!
@IBOutlet weak var movieTitle: UILabel!
@IBOutlet weak var movieOverview: UILabel!
var titlemovie = ""
var overview = ""
var imagemovie = UIImage()
override func viewDidLoad() {
super.viewDidLoad()
movieTitle.text = titlemovie
movieOverview.text = overview
movieImage.image = imagemovie
}
}
CodePudding user response:
You need to return UIImage :
class MoviePresenter: MoviePresenterProtocol {
...
// Convert data to UIImage
func getImageData(by row: Int) -> UIImage? {
return UIImage(data: cache[row])
}
You need yo use UIImage and UIImage view to configure cell :
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: MovieViewCell.identifier, for: indexPath) as! MovieViewCell
let row = indexPath.row
let title = presenter.getTitle(by: row)
let overview = presenter.getOverview(by: row)
let image = presenter.getImageData(by: row)
// cell is now configured with an image
cell.configureCell(title: title, overview: overview, image: image)
return cell
}
You need to modify cell.configureCell to handle UIImage? instead of data.
When selecting a cell you must use UIImage to init VC :
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let dc = storyboard?.instantiateViewController(withIdentifier: "MovieDeatilsViewController") as! MovieDeatilsViewController
let row = indexPath.row
dc.titlemovie = presenter.getTitle(by: row) ?? ""
dc.overview = presenter.getOverview(by: row) ?? ""
dc.imagemovie = presenter.getImageData(by: row)// now an image
self.navigationController?.pushViewController(dc, animated: true)
}
Use UIImage to initialise image movie in detail vc :
class MovieDeatilsViewController: UIViewController {
@IBOutlet weak var movieImageView: UIImageView! // Image view here
@IBOutlet weak var movieTitle: UILabel!
@IBOutlet weak var movieOverview: UILabel!
var titlemovie = ""
var overview = ""
var imagemovie : UIImage?
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated: animated)
movieTitle.text = titlemovie
movieOverview.text = overview
// here you could also display a default image
// if image is not set
if let image = imagemovie {
movieImageView.image = image
}
}
}