Swift 5 - Set TextLabel in Custom CollectionviewCell from JSON Array - ios

I have created a custom cell for my collectionview that i have set in a tableview for my app. I need to know to to set the text label to appear as the items in my array that is listed in my JSON File that is local.
View Controller:
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var workoutData = [Models]()
#IBOutlet weak var tableview: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
parseJSON()
tableview.register(CollectionTableViewCell.nib(), forCellReuseIdentifier: CollectionTableViewCell.identifier)
print(workoutData)
}
func numberOfSections(in tableView: UITableView) -> Int {
return workoutData.count
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return workoutData[section].title
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return workoutData[section].workouts.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: CollectionTableViewCell.identifier, for: indexPath) as! CollectionTableViewCell
cell.configure(with: workoutData)
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableview.deselectRow(at: indexPath, animated: true)
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 250.0
}
func parseJSON() {
let url = Bundle.main.url(forResource: "data", withExtension: "json")!
do{
let data = try Data(contentsOf: url)
workoutData = try JSONDecoder().decode([Models].self, from: data)
} catch {
print(error)
}
}
}
My Custom Cell File:
import UIKit
class MyCollectionViewCell: UICollectionViewCell {
var workoutData = [Models]()
#IBOutlet weak var myLabel: UILabel!
static let identifier = "MyCollectionViewCell"
static func nib() -> UINib {
return UINib(nibName: "MyCollectionViewCell", bundle: nil)
}
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
public func configure(with model: Models) {
self.myLabel.text = //what to put here.
print(model.title)
}
}
My JSON File:
[
{
"title": "Chest",
"workouts": [
"Bench",
"Pushup",
"Incline Press",
"Decline Press",
]
},
{
"title": "Back",
"workouts": [
"Barbell Row",
"Lat Pulldown",
"Deadlift",
"Back Extension",
]
},
{
"title": "Arms",
"workouts": [
"Barbell Curl",
"Dumbbell Curl",
"Tricep Pressdown",
"Skull Crusher",
]
}
]
I want my text label to show the items in my workouts array. when i set it i get the error "cannot assign value of type '[String]' to type 'String'". I would appreciate any help or directions. Thanks
EDIT:
I am looking to build my layout similar to the horizontal scroll of the the app store

workouts is an array of String. So firstly you need to get the String from array by index.
You can use this to show the first value on the label
self.myLabel.text = models.workouts[0]
Or If you want to show all the workouts values on the array then you can use
self.myLabel.text = models.workouts.joined(separator: ", ")

Related

How to use skeleton loading with multiple sections in swift?

I want to apply skeleton animation with tableview. To achieve this I am using 'SkeletonView' cocopods and it's working fine with single section but when I am trying with multiple section it's throwing out of bounds error. Even i don't know from where I could find documentation for this cocopods. If anybody has some idea please help me out.
import UIKit
import SkeletonView
class ViewController: UIViewController {
#IBOutlet weak var tableView: UITableView!
var data = [[String: Any]]()
override func viewDidLoad() {
super.viewDidLoad()
tableView.rowHeight = 80
tableView.estimatedRowHeight = 80
DispatchQueue.main.asyncAfter(deadline: .now() + 5, execute: {
self.data.append(["day": "Mon",
"record": [
["name": "Abhya"], ["name": "Anivesh"]
]
])
self.data.append(["day": "Tue",
"record": [
["name": "Vivek"], ["name": "Arun"]
]
])
self.data.append(["day": "Wed",
"record": [
["name": "Bindu"], ["name": "Aliya"]
]
])
self.data.append(["day": "Thi",
"record": [
["name": "Vivek"], ["name": "Arun"]
]
])
print(self.data[0])
self.tableView.stopSkeletonAnimation()
self.view.hideSkeleton()
self.tableView.reloadData()
})
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
tableView.isSkeletonable = true
tableView.showAnimatedGradientSkeleton()
tableView.showAnimatedSkeleton(usingColor: .concrete, transition: .crossDissolve(0.25))
}
func getArray(withDictionary array: Any?) -> [Dictionary<String, Any>] {
guard let arr = array as? [Dictionary<String, Any>] else {
return []
}
return arr
}
}
extension ViewController:UITableViewDelegate {
}
extension ViewController: SkeletonTableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
self.data.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let dictionary = self.data[section]
let array = getArray(withDictionary: dictionary["record"])
return array.count
}
func collectionSkeletonView(_ skeletonView: UITableView, cellIdentifierForRowAt indexPath: IndexPath) -> ReusableCellIdentifier {
return "TeacherCell"
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 60
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let cell = tableView.dequeueReusableCell(withIdentifier: "HeaderCell") as! HeaderTableViewCell
print("section", section)
let dict = self.data[section]
cell.dayName.text = dict["day"] as! String
return cell
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "TeacherCell") as! TeacherTableViewCell
let dictionary = self.data[indexPath.row]
let array = getArray(withDictionary: dictionary["record"])
let dict = array[indexPath.row]
cell.teacherName.text = dictionary["name"] as! String
return cell
}
}
I had the same issue and solved it in the tableView Cell.
In awakeNib() add: someLabel.showAnimatedSkeleton()
After that add a custom function:
func hideSkeleton(){
someLabel.hideSkeleton()
}
Now in Your MainView Controller call this function in cellForItem just before return cell.
Hope this works for you.

Outputting element from two-dimensional array from separate class

I need to output the second element from a two-dimensional array from one class to a TableViewController. I've tried a couple things, such as data.reduce into:, with no luck. The FirstViewController is supposed to be populated with the three genres "Action, Suspense and Romance", which can then be clicked on to show all the movies that belong to that genre.
First Class:
import Foundation
protocol MovieModelDelegate: class {
func didRecieveDataUpdate(data: [[String]])
}
class MovieModel {
weak var delegate: MovieModelDelegate?
var genreArray: [String] = []
let data: [[String]] = [
["Avatar", "Action"],
["Limitless", "Suspense"],
["Die Hard", "Action"],
["The Avengers", "Action"],
["The Notebook", "Romance"],
["Lakehouse", "Romance"],
["Gone Girl", "Suspense"],
["Deadpool", "Action"],
["Passengers", "Suspense"],
["Inception", "Suspense"],
["Mission Impossible", "Action"]
]
func requestData() {
let movie: [[String]] = data
delegate?.didRecieveDataUpdate(data: movie)
}
}
**TableView Class:**
class FirstTableView: UITableViewController, MovieModelDelegate {
var model = MovieModel()
var selectedGenre: String = ""
override func viewDidLoad() {
super.viewDidLoad()
model.delegate = self
model.requestData()
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return model.genreArray.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "GenreCells", for: indexPath) as! TableViewCell
cell.genreLabel?.text = model.genreArray[indexPath.item]
return cell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
selectedGenre = model.data[indexPath.row]
selectedGenre = ""
for indexPath in model.data{
if indexPath[1] == selectedGenre{
selectedGenre.append(indexPath[0])
}
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if (segue.identifier == "pushToMovies") {
if let VC = segue.destination as? FilteredSelection {
VC.selectedMovies = [selectedGenre]
}
}
}
func didRecieveDataUpdate(data: [[String]]) {
}
deinit{
}
}
You need to change the data into a form that can then be indexed by a tableview. I would change it into a dictionary using the following code.
let genreDictionary = data.reduce(into: [:]) { d, element in
d[element[1], default: []].append(element[0])
}
This will create the following Dictionary
["Suspense": ["Limitless", "Gone Girl", "Passengers", "Inception"],
"Romance": ["The Notebook", "Lakehouse"],
"Action": ["Avatar", "Die Hard", "The Avengers", "Deadpool", "Mission Impossible"]]
Then in your tableview functions you use the dictionary as follows
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return model.genreDictionary.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "GenreCells", for: indexPath) as! TableViewCell
let d = model.genreDictionary
cell.genreLabel?.text = d[d.keys.index(d.startIndex, offsetBy: indexPath.row)].key
return cell
}
When you need the array of films for a certain tableview row you can use the following
let d = model.genreDictionary
let arrayOfFilms = d[d.keys.index(d.startIndex, offsetBy: indexPath.row)].value
You can use a Set to get all Genres without to have duplicates.
I recognized you want to display the genres in your TableView with model.genreArray but your genreArray is always empty because you never append data to it.
The second thing I recoginzed: In your TableViewController you
implemented the didReciveDataUpdate function, but you didnt do
anything with it.
First Class:
import Foundation
protocol MovieModelDelegate: class {
func didRecieveDataUpdate(data: [[String]])
}
class MovieModel {
weak var delegate: MovieModelDelegate?
let data: [[String]] = [
["Avatar", "Action"],
["Limitless", "Suspense"],
["Die Hard", "Action"],
["The Avengers", "Action"],
["The Notebook", "Romance"],
["Lakehouse", "Romance"],
["Gone Girl", "Suspense"],
["Deadpool", "Action"],
["Passengers", "Suspense"],
["Inception", "Suspense"],
["Mission Impossible", "Action"]
]
private var genres = [String]()
public init() {
for dataSet in data {
self.genres.append(dataSet[1])
}
//this will generate a array with only uniqe genres
var genreArray = Array(Set(self.genres))
}
func requestData() {
let movie: [[String]] = data
delegate?.didRecieveDataUpdate(data: movie)
}
}
FirstTableView Class:
(view didReciveDataUpdate function)
class FirstTableView: UITableViewController, MovieModelDelegate {
var model = MovieModel()
var selectedGenre: String = ""
override func viewDidLoad() {
super.viewDidLoad()
model.delegate = self
model.requestData()
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return model.genreArray.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "GenreCells", for: indexPath) as! TableViewCell
cell.genreLabel?.text = model.genreArray[indexPath.row]
return cell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
//To get the Genre
selectedGenre = model.data[indexPath.row][1]
for indexPath in model.data{
if indexPath[1] == selectedGenre{
selectedGenre.append(indexPath[0])
}
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if (segue.identifier == "pushToMovies") {
if let VC = segue.destination as? FilteredSelection {
VC.selectedMovies = [selectedGenre]
}
}
}
func didRecieveDataUpdate(data: [[String]]) {
//=================here you need to update your tableView===================
}
deinit{
}
}

how can i know if my array is consuming the data i try to send to it? swift 4

hi i am new to this swift and coding in general, i have created a tiny app that the user can take a photo form the photo library and type in a title and a location of the image and upload it to the tableview, but i have some problem and i dont really know what is the problem because iam new to this but there is no error in the code or warnings there is only problem really when the user press Done/Uploadbutton nothing happens, my tableview dont update. Iam not sure if my tableview is not updating or if i even am sending the data over to my array...
so how can i see what data is in my array and if iam even sending the data the right way?
Here is how iam inserting the datato the array
#IBAction func doneEditing(_ sender: Any) {
insertNewActy()
}
func insertNewActy(){
addTitles = addTitle.text!
addLocations = addLocation.text!
newImages = newImage.image!
let element = MyCellRows(image: newImages,
title: addTitles,
location: addLocations)
myCellRows.insert(element, at: 0)
}
and here is the code of my array: is my Array even set up the right way?
import UIKit
var myCellRows: [MyCellRows] = []
class ActyViewController: UIViewController {
#IBOutlet weak var myTableView: UITableView!
func createMyCellArray() -> [MyCellRows] {
var myCells: [MyCellRows] = []
let dog = MyCellRows(image: #imageLiteral(resourceName: "Dog"),
title: "Dog",
location: "America")
let cat = MyCellRows(image: #imageLiteral(resourceName: "Cat"),
title: "Cat",
location: "Sweden")
let rabbit = MyCellRows(image: #imageLiteral(resourceName: "Rabbit"),
title: "Rabbit",
location: "Germany")
let tiger = MyCellRows(image: #imageLiteral(resourceName: "Tiger"),
title: "Tiger",
location: "Africa")
myCells.append(dog)
myCells.append(cat)
myCells.append(rabbit)
myCells.append(tiger)
return myCells
}
override func viewDidLoad() {
super.viewDidLoad()
myCellRows = createMyCellArray()
// Do any additional setup after loading the view.
}
}
this is my tableView code
extension ActyViewController: UITableViewDataSource, UITableViewDelegate{
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return myCellRows.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let myActy = myCellRows[indexPath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as! ActyCellTableViewCell
cell.setActy(actys: myActy)
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 200.0
}
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == UITableViewCellEditingStyle.delete{
myCellRows.remove(at: indexPath.row)
myTableView.reloadData()
}
}
override func viewDidAppear(_ animated: Bool){
super.viewDidAppear(true)
myTableView.reloadData()
}
}
I think you have confused your concepts a bit but you explained your problem well so you're off to a good start. I've rewritten parts of your code and left out the image part so you have a working table view before you continue:
struct Animal {
var title: String
var location: String
}
class ActyViewController: UIViewController, UITableViewDataSource {
var animals: [Animal] = [
Animal(title: "Dog", location: "America"),
Animal(title: "Cat", location: "Sweden"),
Animal(title: "Rabbit", location: "Germany"),
Animal(title: "Tiger", location: "Africa"),
]
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return animals.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cellIdentifier = "AnimalCell"
let cell: UITableViewCell = {
guard let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier) else {
return UITableViewCell(style:.subtitle, reuseIdentifier: cellIdentifier)
}
return cell
}()
cell.textLabel?.text = animals[indexPath.row].title
cell.detailTextLabel?.text = animals[indexPath.row].location
return cell
}
}
As you can see above, you should separate your data from how you present your data, so I've made an array of Animal that keeps your data. Your tableview should have a UITableViewDataSource and will ask this data source for data using the two functions you can see.
Now you can add an animal to your list of animals and refresh the table
animals.append(Animal(title: "Elephant", location: "India"))
tableView.reloadData()
Hope that helps to get you going.
If your only problem is that your tableView isn't refreshing after you add the new data, make sure you call reloadData() after you update your array. If your insertNewActy() function is in the same ViewController as the rest of your code you should be able to change it to:
func insertNewActy(){
addTitles = addTitle.text!
addLocations = addLocation.text!
newImages = newImage.image!
let element = MyCellRows(image: newImages,
title: addTitles,
location: addLocations)
myCellRows.insert(element, at: 0)
myTableView.reloadData()
}

Infinite scroll with UITableView and an Array

I'm new in Swift and I want to make an infinite scroll with an Array. Here it's my class TableViewController
class TableViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var legumes: [String] = ["Eggs", "Milk", "Chocolat", "Web", "Miel", "Pop", "Eco", "Moutarde", "Mayo", "Thea", "Pomelade", "Gear", "Etc" , "Nop", "Dews", "Tout", "Fun", "Xen" , "Yoga" ]
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.delegate = self
self.tableView.dataSource = self
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.legumes.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = self.tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! ImageTableViewCell
return cell
}
}
I want to show the first ten items of my array and when I am on the bottom of the TableViewController, it will load the next ten items, etc. I don't know how to do, I see a lot of code on GitHub but I don't know how to implement them.
Thank you so much.
Consider using PagingTableView
class MainViewController: UIViewController {
#IBOutlet weak var contentTable: PagingTableView!
var legumes: [String] = ["Eggs", "Milk", "Chocolat", "Web", "Miel", "Pop", "Eco", "Moutarde", "Mayo", "Thea", "Pomelade", "Gear", "Etc" , "Nop", "Dews", "Tout", "Fun", "Xen" , "Yoga" ]
override func viewDidLoad() {
super.viewDidLoad()
contentTable.dataSource = self
contentTable.pagingDelegate = self
}
}
extension MainViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return legumes.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! ImageTableViewCell
guard legumes.indices.contains(indexPath.row) else { return cell }
cell.content = legumes[indexPath.row]
return cell
}
}
extension MainViewController: PagingTableViewDelegate {
func paginate(_ tableView: PagingTableView, to page: Int) {
contentTable.isLoading = true
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
self.legumes.append(contentsOf: legumes)
self.contentTable.isLoading = false
}
}
}
Modify the paginate function to work as you wish
What you're describing is called pagination. You should do something like this:
/* Number of page you're loading contents from */
var pageIndex: Int = 1
override func scrollViewDidScroll(scrollView: UIScrollView) {
let offsetY = scrollView.contentOffset.y
let contentHeight = scrollView.contentSize.height
if offsetY > contentHeight - scrollView.frame.size.height {
/* increment page index to load new data set from */
pageIndex += 1
/* call API to load data from next page or just add dummy data to your datasource */
/* Needs to be implemented */
loadNewItemsFrom(pageIndex)
/* reload tableview with new data */
tableView.reloadData()
}
}

Swift 3 pass the cell data A view to B view, with fun didSelectRowAt indexPath crash after select row

I want to pass the data A view to B view, it can build and show data in A view, but after I selected the cell, it crashed. And it shows the problem on the code.
vcTwo.selectedzones.zones = [selectedCity]
fatal error: unexpectedly found nil while unwrapping an Optional value (lldb)
my code
The struct mode:
struct Location {
var city: String!
var zones = [String]()
}
var city = ["KHT", "TPAP", "TNNY"]
let kh = Location.init(city: "KHT", zones: ["sami", "zami", "zomi", "komi", "shini"])
let tpa = Location.init(city: "TPAP", zones: ["mid", "east", "anci", "zochi"])
let tnn = Location.init(city: "TNNY", zones: ["TN1","TN2", "TN3", "TN4", "TN5"])
Here is the A viewController code:
import UIKit
class FirstViewTableViewController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return city.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! FirstCell
cell.firstLabel.text = city[indexPath.row]
return cell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let selectedCity = city[indexPath.row]
let vcTwo = self.storyboard?.instantiateViewController(withIdentifier: "secondVC") as! secondViewController
vcTwo.selectedzones.zones = [selectedCity]
self.navigationController?.pushViewController(vcTwo, animated: true)
}
}
The B viewController:
import UIKit
class secondViewController: UITableViewController {
var selectedzones: Location!
override func viewDidLoad() {
super.viewDidLoad()
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return selectedzones.zones.count
}
Is there any part wrong in the func didSelectRowAt indexPath?
here is the problem
vcTwo.selectedzones.zones = [selectedCity]
you need to init the selectedZone in first Controller
let kh = Location.init(city: "KHT", zones: ["sami", "zami", "zomi", "komi", "shini"])
vcTwo.selectedzones = kh
Try this
class secondViewController: UITableViewController {
var selectedzones = Location()
}
you are try to accesss zones but you have not allocate Location so it will crash.
Hope it will help you
Instead of Optional ! use Optional ?
class secondViewController: UITableViewController {
var selectedzones: Location?
NOT
class secondViewController: UITableViewController {
var selectedzones: Location!

Resources