I'm trying to send an array from TableView to another TableView by using a protocol but when I'm trying to show data in the second TableView I got 1 element only in it, not the whole array.
From the model, I'm passing name in ExtendedIngredient array from HomeView to be displayed in the TableView in RecipesDetailsView
As in the above image, the ExtendedIngredient has 10 elements but it only shows 1 "all-purpose flour" and I want to show all 10 items.
Model:
struct Recipes: Codable {
let recipes: [Recipe]
}
struct Recipe: Codable {
let title: String?
let image: String?
let pricePerServing: Double?
let readyInMinutes, servings: Int?
let instructions: String?
let extendedIngredients: [ExtendedIngredient]
}
struct ExtendedIngredient: Codable {
let id: Int?
let aisle, image: String?
let name, original, originalString, originalName: String?
let amount: Double?
let unit: String?
let meta, metaInformation: [String]
}
HomeView:
protocol RecipesDetailsSelectActionDelegate: class {
func recipeDetails(
recipeTitle: String,
recipeImage: String,
recipeInstructions: String,
ingredientsNumber: String,
ingredientsNumbersInt: Int,
ingredientsName: [String]
)
}
class HomeView: UIView {
var recipes: Recipes?
var recipesDetails = [Recipe]()
weak var recipeDetailsViewSelectActionDelegate: RecipesDetailsSelectActionDelegate?
override init( frame: CGRect) {
super.init(frame: frame)
layoutUI()
}
lazy var foodTableView: UITableView = {
let foodTableView = UITableView()
foodTableView.translatesAutoresizingMaskIntoConstraints = false
foodTableView.backgroundColor = .customVeryLightGray()
foodTableView.delegate = self
foodTableView.dataSource = self
foodTableView.register(HomeTableViewCell.self, forCellReuseIdentifier: "HomeTableViewCell")
foodTableView.rowHeight = UITableView.automaticDimension
foodTableView.estimatedRowHeight = 100
foodTableView.showsVerticalScrollIndicator = false
foodTableView.separatorStyle = .none
return foodTableView
}()
}
extension HomeView: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
recipeDetailsViewSelectActionDelegate?.recipeDetails(
recipeTitle: recipesDetails[indexPath.row].title ?? "Error",
recipeImage: recipesDetails[indexPath.row].image ?? "Error",
recipeInstructions: recipesDetails[indexPath.row].instructions ?? "Error",
ingredientsNumber: "\(recipesDetails[indexPath.row].extendedIngredients.count)",
ingredientsNumbersInt: recipesDetails[indexPath.row].extendedIngredients.count,
ingredientsName: [(recipesDetails[indexPath.row].extendedIngredients[indexPath.row].name ?? "")]
)
}
}
RecipesDetailsView:
class RecipesDetailsView: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
layoutUI()
}
lazy var tableView: UITableView = {
let tableView = UITableView()
tableView.translatesAutoresizingMaskIntoConstraints = false
tableView.delegate = self
tableView.dataSource = self
tableView.rowHeight = UITableView.automaticDimension
tableView.estimatedRowHeight = 100
tableView.showsVerticalScrollIndicator = false
tableView.separatorStyle = .none
tableView.backgroundColor = .white
tableView.register(NumberOfIngredientsTableViewCell.self, forCellReuseIdentifier: "NumberOfIngredientsTableViewCell")
return tableView
}()
}
extension RecipesDetailsView: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "NumberOfIngredientsTableViewCell", for: indexPath) as! NumberOfIngredientsTableViewCell
cell.theNameOfIngredient.text = recipeVC.ingredientsName[indexPath.row]
return cell
}
}
HomeViewController:
class HomeViewController: UIViewController {
lazy var mainView: HomeView = {
let view = HomeView(frame: self.view.frame)
view.recipeDetailsViewSelectActionDelegate = self
return view
}()
}
extension HomeViewController: RecipesDetailsSelectActionDelegate {
func recipeDetails(recipeTitle: String, recipeImage: String, recipeInstructions: String, ingredientsNumber: String, ingredientsNumbersInt: Int, ingredientsName: [String]) {
let vc = RecipesDetailsViewController()
vc.recipeTitle = recipeTitle
vc.recipeImage = recipeImage
vc.recipeInstructions = recipeInstructions
vc.ingredientsNumber = ingredientsNumber
vc.ingredientsNumberInt = ingredientsNumbersInt
vc.ingredientsName = ingredientsName
self.show(vc, sender: nil)
}
}
RecipesDetailsViewController:
class RecipesDetailsViewController: UIViewController {
var recipeTitle: String?
var recipeImage: String?
var recipeInstructions: String?
var ingredientsNumber: String?
var ingredientsNumberInt: Int?
var ingredientsName: [String] = []
lazy var mainView: RecipesDetailsView = {
let view = RecipesDetailsView(frame: self.view.frame)
view.backgroundColor = .white
return view
}()
override func loadView() {
super.loadView()
mainView.recipeVC = self
view = mainView
}
}
The problem lies in the following line:
ingredientsName:[(recipesDetails[indexPath.row].extendedIngredients[indexPath.row].name ?? "")]
"extendedIngredients[indexPath.row]" this expression will always return just one object depending upon the selected row.
You might have to do something like:
(recipesDetails[indexPath.row].extendedIngredients.compactMap({$0.name}))
Related
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) // Error on this line**
self.navigationController?.pushViewController(dc, animated: true)
}
}
/*
var titlemoview = ""
var overview = ""
var imagemovie = UIImage()
*/
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
}
}
Get UIImage from data in configureCell(:, : , :) function by UIImage(data:yourdata) and assign to your imageview
let image = UIImage(data:yourdata)
imageview.image = image
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
}
}
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
}
}
}
I'm trying to pass data from UIView contains UITableView to UIViewController but unfortunately, it doesn't work.
Model:
struct Recipes: Codable {
let recipes: [Recipe]
}
struct Recipe: Codable {
let title: String?
let image: String?
let pricePerServing: Double?
let readyInMinutes, servings: Int?
}
The data should be passed from this view
HomeView:
protocol RecipesDetailsSelectActionDelegate: class {
func recipeDetails(recipeTitle: String)
}
class HomeView: UIView {
var recipes: Recipes?
var recipesDetails = [Recipe]()
let indicator = ActivityIndicator()
weak var homeViewDidSelectActionDelegate: HomeViewDidSelectActionDelegate?
weak var recipeDetailsViewSelectActionDelegate: RecipesDetailsSelectActionDelegate?
override init( frame: CGRect) {
super.init(frame: frame)
layoutUI()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
lazy var foodTableView: UITableView = {
let foodTableView = UITableView()
foodTableView.translatesAutoresizingMaskIntoConstraints = false
foodTableView.backgroundColor = .customVeryLightGray()
foodTableView.delegate = self
foodTableView.dataSource = self
foodTableView.register(HomeTableViewCell.self, forCellReuseIdentifier: "HomeTableViewCell")
foodTableView.rowHeight = UITableView.automaticDimension
foodTableView.estimatedRowHeight = 100
foodTableView.showsVerticalScrollIndicator = false
foodTableView.separatorStyle = .none
return foodTableView
}()
}
extension HomeView: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return recipesDetails.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "HomeTableViewCell", for: indexPath) as! HomeTableViewCell
let url = URL(string: recipesDetails[indexPath.row].image ?? "Error")
cell.foodImage.kf.setImage(with: url)
cell.foodTitle.text = recipesDetails[indexPath.row].title
if let readyInMin = recipesDetails[indexPath.row].readyInMinutes {
cell.cookingTimeInfoLabel.text = "\(readyInMin) Minutes"
}
if let pricePerServing = recipesDetails[indexPath.row].pricePerServing {
cell.priceInfoLabel.text = String(format: "%.2f", pricePerServing / 100)
}
if let serving = recipesDetails[indexPath.row].servings {
cell.servesInfoLabel.text = "\(serving)"
}
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
recipeDetailsViewSelectActionDelegate?.recipeDetails(recipeTitle: recipesDetails[indexPath.row].title ?? "Error")
}
}
HomeViewController:
class HomeViewController: UIViewController {
lazy var mainView: HomeView = {
let view = HomeView(frame: self.view.frame)
view.recipeDetailsViewSelectActionDelegate = self
return view
}()
override func loadView() {
super.loadView()
view = mainView
}
}
extension HomeViewController: RecipesDetailsSelectActionDelegate {
func recipeDetails(recipeTitle: String) {
let vc = RecipesDetailsViewController()
vc.mainView.recipeTitle = recipeTitle
self.show(vc, sender: nil)
}
}
The data should be passed to this view
RecipesDetailsView:
class RecipesDetailsView: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
layoutUI()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
var recipeTitle: String?
lazy var tableView: UITableView = {
let tableView = UITableView()
tableView.translatesAutoresizingMaskIntoConstraints = false
tableView.delegate = self
tableView.dataSource = self
tableView.rowHeight = UITableView.automaticDimension
tableView.estimatedRowHeight = 100
tableView.showsVerticalScrollIndicator = false
tableView.separatorStyle = .none
tableView.backgroundColor = .white
tableView.register(IngredientsTableViewCell.self, forCellReuseIdentifier: "IngredientsTableViewCell")
return tableView
}()
extension RecipesDetailsView: UITableViewDelegate, UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "IngredientsTableViewCell", for: indexPath) as! IngredientsTableViewCell
cell.recipeTitleLabel.text = recipeTitle
return cell
}
}
RecipesDetailsViewController:
class RecipesDetailsViewController: UIViewController {
lazy var mainView: RecipesDetailsView = {
let view = RecipesDetailsView(frame: self.view.frame)
view.backgroundColor = .white
return view
}()
override func loadView() {
super.loadView()
view = mainView
}
}
I have a class MenuTabs: UIView that corresponds to MenuTabs.xib. I linked them in the identity inspector. In the view is a UICollectionView. I set the UIView as the delegate and datasource for the collection view in storyboard. I'm using the MenuTabs class in a ViewController, but I keep getting this error
'NSInvalidArgumentException', reason: '-[Workout_Tracker.QuickAddViewController collectionView:numberOfItemsInSection:]: unrecognized selector sent to instance 0x7fbbe970a120'
Here are my MenuTabs and QuickAddViewController files
import UIKit
class MenuTabs: UIView {
let workoutTypes = ["", "", "", ""]
let cellId = "cellId"
#IBOutlet weak var contentView: UIView!
#IBOutlet weak var collectionView: UICollectionView!
override init(frame: CGRect) {
super.init(frame: frame)
collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: cellId)
setup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
if self.subviews.count == 0 {
self.setup()
}
}
func setup() {
Bundle.main.loadNibNamed("MenuTabs", owner: self, options: nil)
guard let content = contentView else { return }
contentView.frame = self.bounds
contentView.autoresizingMask = [.flexibleHeight, .flexibleWidth]
addSubview(content)
}
}
// MARK: - Delegate and Datasource methods for UICollectionView
extension MenuTabs: UICollectionViewDelegate, UICollectionViewDataSource,
UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return workoutTypes.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath)
cell.backgroundColor = UIColor.red
return cell
}
}
import UIKit
class QuickAddViewController: UIViewController {
enum MuscleGroup: String {
case abs = "Abs"
case arms = "Arms"
case back = "Back"
case calves = "Calves"
case chest = "Chest"
case legs = "Glutes & Legs"
case shoulders = "Shoulders"
}
enum WorkoutType: String {
case bodyWeight = "Body Weight"
case weightTraining = "Weight Training"
case sportsAndRecreation = "Sports & Recreation"
case cardio = "Cardio"
}
#IBOutlet weak var workoutTypesMenu: UIView!
let exercisesData = ExerciseDatabase()
var workoutTypesDictionary = Dictionary<String,Dictionary<String,Array<String>>>()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
workoutTypesDictionary = self.exercisesData.exercisesByWorkoutType
tabBarController?.tabBar.isTranslucent = false
// Load workoutTypesMenu View
if let wtMenu = Bundle.main.loadNibNamed("MenuTabs", owner: self, options: nil)?.first as! MenuTabs? {
workoutTypesMenu.addSubview(wtMenu)
}
}
// MARK: - Get data from ExerciseDatabase.swift
// Get the workout types
func getWorkoutTypes() -> [String] {
var workoutTypesArray : [String] = []
for workoutType in workoutTypesDictionary.keys {
workoutTypesArray.append(workoutType)
}
return workoutTypesArray
}
// Get the list of muscles or options
func getMusclesOrOptions(for workoutType: String) -> [String] {
var musclesOrOptionsArray : [String] = []
let musclesOrOptions = workoutTypesDictionary[workoutType]!.keys
for muscleOrOption in musclesOrOptions {
musclesOrOptionsArray.append(muscleOrOption)
}
return musclesOrOptionsArray
}
// Get the list of exercises
func getExercisesArray(for workoutType: String, for muscleOrOption: String) -> [String] {
var exercisesArray : [String] = []
exercisesArray = workoutTypesDictionary[workoutType]![muscleOrOption]!
return exercisesArray
}
// Get the selected exercise
func getSelectedExercise(in workoutType: String, for muscleOrOption: String, at index: Int) -> String {
var selectedExercise : String = ""
selectedExercise = workoutTypesDictionary[workoutType]![muscleOrOption]![index]
return selectedExercise
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - WorkoutTypesBar and Muscles and Options Bar
// Set up the WorkoutTypes bar and the muscles and option types bar
}
Instead of setting up the delegate and datasource through the nib file, I set them in the init method of MenuTabs.swift
override init(frame: CGRect) {
super.init(frame: frame)
collectionView.delegate = self
collectionView.dataSource = self
collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: cellId)
setup()
}
My json data is like that and I'm using alamofire for loading data and objectmapper for mapping.I just try to parsing json data and show it on CollectionView.However Im getting error.
Here is my viewcontroller
import UIKit
import Alamofire
import ObjectMapper
import AlamofireObjectMapper
class ViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return schedules?.count ?? 0
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = UICollectionViewCell()
cell.textLabel?.text = schedules?[indexPath.row].title
}
#IBOutlet weak var collectionViewData: UICollectionView!
var schedules: [Schedule]?
override func viewDidLoad() {
super.viewDidLoad()
collectionViewData.dataSource = self
collectionViewData.delegate = self
loadData()
}
func loadData() {
let jsonDataUrl = "https://jsonplaceholder.typicode.com/posts"
Alamofire.request(jsonDataUrl).responseJSON { response in
self.schedules = Mapper<Schedule>().mapArray(JSONObject: response.result.value)
self.collectionViewData.reloadData()
}
}
}
Here is my schedule file codes for mapping.
import Foundation
import ObjectMapper
import AlamofireObjectMapper
class Schedule: Mappable {
var userId: String
var id: String
var title: String
var body: String
required init?(map: Map) {
userId = ""
id = ""
title = ""
body = ""
}
func mapping(map: Map) {
userId <- map["userId"]
id <- map["id"]
title <- map["title"]
body <- map["body"]
}
}
When I try to run it I'm getting error like that: Value of type 'UICollectionViewCell' has no member 'textLabel'
I tried to add "textLabel" to viewcontroller but it didn't work.Should I add a new class for CollectionView?
Here is a collectionview cell class you can use that has textLabel
class MyCollectionView: UICollectionViewCell {
var textLabel: UILabel!
override init(frame: CGRect) {
super.init(frame: frame)
createViews()
}
required init?(coder aDecoder: NSCoder) {
fatalError("Cannot initialize")
}
func createViews() {
textLabel = UILabel(frame: .zero)
textLabel.textAlignment = .center
textLabel.translatesAutoresizingMaskIntoConstraints = false
textLabel.backgroundColor = UIColor.black
textLabel.textColor = UIColor.white
textLabel.font = Font.menuText
contentView.addSubview(textLabel)
let views: [String: Any] = ["label": textLabel]
let lHFormat = "H:|[label]|"
let lVFormat = "V:[label]|"
[lHFormat, lVFormat].forEach { format in
let constraints = NSLayoutConstraint.constraints(withVisualFormat: format,
options: [],
metrics: nil,
views: views)
contentView.addConstraints(constraints)
}
}
You can now register this cell and use it so that you can use textLabel property.