How to change data in UICollectionViewCell's? - ios

In the application I'm currently working on, I need to use several different UICollectionViewCells. The question is, how to correctly implement cells in which, depending on the tab selected in the top menu, it will change how it looks and have different input and output data? This is how it looks now. And example of cell on another tab.
Here is the code:
DataModel:
enum Category: Int, CustomStringConvertible, CaseIterable {
case places = 0
case routes
case events
case garbagePoints
...
}
struct ExploreCardData {
let cardNameTitle: String
let category: Category
let cardPlacesImage: UIImage?
let cardLocationName: String
let cardLocationIcon: UIImage?
let cardTransportIcon: UIImage?
let cardTransportLabel: String
let cardBeautyIcon: UIImage?
let cardBeautyLabel: String
let cardPollutionIcon: UIImage?
let cardPollutionLabel: String
}
var exploreCardData: [ExploreCardData] {
var myExploreCardData: [ExploreCardData] = []
myExploreCardData.append(ExploreCardData(cardNameTitle: "Name", category: .places, cardPlacesImage: UIImage(named: "NoImage")!, cardLocationName: "Locality", cardLocationIcon: UIImage(named: "locationIcon")!, cardTransportIcon: UIImage(named: "transportIcon")!, cardTransportLabel: "0,0", cardBeautyIcon: UIImage(named: "beautyIcon")!, cardBeautyLabel: "0,0", cardPollutionIcon: UIImage(named: "pollutionIcon")!, cardPollutionLabel: "0,0"))
myExploreCardData.append(ExploreCardData(cardNameTitle: "Name", category: .routes, cardPlacesImage: UIImage(named: "NoImage")!, cardLocationName: "Start/Finish", cardLocationIcon: UIImage(named: "locationIcon")!, cardTransportIcon: UIImage(named: "transportIcon")!, cardTransportLabel: "0,0", cardBeautyIcon: UIImage(named: "beautyIcon")!, cardBeautyLabel: "0,0", cardPollutionIcon: UIImage(named: "pollutionIcon")!, cardPollutionLabel: "0,0"))
myExploreCardData.append(ExploreCardData(cardNameTitle: "Name", category: .events, cardPlacesImage: UIImage(named: "NoImage")!, cardLocationName: "Locality", cardLocationIcon: UIImage(named: "locationIcon"), cardTransportIcon: UIImage(named: ""), cardTransportLabel: "0,0", cardBeautyIcon: UIImage(named: ""), cardBeautyLabel: "0,0", cardPollutionIcon: UIImage(named: ""), cardPollutionLabel: "0,0"))
myExploreCardData.append(ExploreCardData(cardNameTitle: "Name", category: .garbagePoints, cardPlacesImage: UIImage(named: "NoImage")!, cardLocationName: "Locality", cardLocationIcon: UIImage(named: "locationIcon")!, cardTransportIcon: UIImage(named: "transportIcon")!, cardTransportLabel: "0,0", cardBeautyIcon: UIImage(named: "beautyIcon")!, cardBeautyLabel: "0,0", cardPollutionIcon: UIImage(named: "pollutionIcon")!, cardPollutionLabel: "0,0"))
return myExploreCardData
}
View Controller:
let collectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
cv.translatesAutoresizingMaskIntoConstraints = false
layout.scrollDirection = .vertical
cv.backgroundColor = .none
return cv
}()
var selectedCategory: Category = .places
var offersForSelectedCategory: [ExploreCardData] {
return exploreCardData.filter { $0.category == selectedCategory }.sorted { $0.cardNameTitle < $1.cardNameTitle }
}
override func viewDidLoad() {
super.viewDidLoad()
setup()
layout()
loadData()
}
extension ScrollableTabBarViewController: ScrollableTabViewDelegate {
/// When tab is tapped reload data.
func scrollableTabView(_ tabView: ScrollableTabView, didTapTabAt index: Int) {
guard index < Category.allCases.count else { return }
selectedCategory = Category.allCases.filter { $0.rawValue == index }.first!
collectionView.reloadData()
}
}
extension ScrollableTabBarViewController: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return offersForSelectedCategory.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let offer = offersForSelectedCategory[indexPath.row]
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as? CollectionViewCell else { return UICollectionViewCell()}
cell.cardPlacesImage.image = offer.cardPlacesImage
cell.cardNameTitle.text = offer.cardNameTitle
cell.cardLocationName.text = offer.cardLocationName
cell.cardLocationIcon.image = offer.cardLocationIcon
cell.cardTransportIcon.image = offer.cardTransportIcon
cell.cardTransportLabel.text = offer.cardTransportLabel
cell.cardBeautyIcon.image = offer.cardBeautyIcon
cell.cardBeautyLabel.text = offer.cardBeautyLabel
cell.cardPollutionIcon.image = offer.cardPollutionIcon
cell.cardPollutionLabel.text = offer.cardPollutionLabel
return cell
}
}

Related

Checkout shopping cart UICollectionViewCell

I have a shopping cart check out flow using a UICollectionView with full page UICollectionViewCells. When the add button is pressed, the remove button is then visible and vice versa. For some reason when add and remove are repeatedly pressed it disrupts the other cells. It will show remove button on another cell when the add button on that cell was never even pressed. I am not sure what's wrong with my logic.
protocol PostCellDelegate {
func removeButtonTapped(cell: PostCell)
func addTapped(cell: PostCell)
}
class PostCell: UICollectionViewCell {
var currentPrice: Float = 0
var delegate: PostCellDelegate?
func set(name: String, brand: String, price: String, image: String){
nameLabel.text = name
brandLabel.text = brand
priceLabel.text = "$\(price)"
photoImageView.loadImage(urlString: image)
}
override init(frame: CGRect) {
super.init(frame: frame)
self.myButton.addTarget(self, action: #selector(addButtonTapped(sender:)), for: .touchUpInside)
self.removeButton.addTarget(self, action: #selector(subButtonTapped(sender:)), for: .touchUpInside)
self.contentView.addSubview(containerView)
setupCellConstraints()
}
#objc func addButtonTapped(sender: UIButton){
self.delegate?.addTapped(cell: self)
sender.isHidden = true
}
#objc func subButtonTapped(sender: UIButton){
self.delegate?.removeButtonTapped(cell: self)
sender.isHidden = true
}
}
class CollectionViewController: UICollectionViewController, UICollectionViewDelegateFlowLayout, PostCellDelegate {
var totalPrice = Float()
private var hiddenRows = Set<Int>()
var finalList = [Item]()
#objc func addTapped(cell: PostCell) {
guard let indexPath = self.collectionView.indexPath(for: cell) else {return}
hiddenRows.insert(indexPath.row)
cell.removeButton.isHidden = false
let item = itemsArr[indexPath.row]
finalList.append(item)
collectionView?.reloadData()
totalPrice += Float(item.price) ?? 0
}
#objc func removeButtonTapped(cell: PostCell) {
guard let indexPath = self.collectionView.indexPath(for: cell) else {return}
hiddenRows.insert(indexPath.row)
cell.myButton.isHidden = false
let item = itemsArr[indexPath.row]
finalList.removeAll{ $0.name == item.name}
totalPrice -= Float(item.price) ?? 0
}
extension CollectionViewController {
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cellId", for: indexPath) as! PostCell
cell.delegate = self
let item = itemsArr[indexPath.row]
let page = itemsArr[indexPath.item]
cell.set(name: item.name, brand: item.brand, price: item.price, image: item.image_url)
if hiddenRows.contains(indexPath.row) {
cell.myButton.isHidden = true
cell.removeButton.isHidden = false
}else{
cell.removeButton.isHidden = true
cell.myButton.isHidden = false
}
return cell
}
Cells are reused you need to restore to default here in cellForItemAt
// restore default look here ( hide / show what you need )
if hiddenRows.contains(indexPath.row) {
cell.myButton.isHidden = true
cell.removeButton.isHidden = false
}else{
cell.removeButton.isHidden = true
cell.myButton.isHidden = false
}
It was because in removeButtonTapped I had hiddenRows.insert(indexPath.row) instead of hiddenRows.remove(indexPath.row). I don't know how I missed that.

Detect last cell in full page UICell UICollectionView

I have a checkout flow using a UICollectionViewController and a full page UICell. After the last cell(the last item) is displayed I want to add a summary page of all the items that have been added to the shopping cart along with a total price. I tried to check if the last item = itemsArray.count - 1 however it keeps printing "last item" in the cell before last. Also in the last cell, it prints "not last".
class CollectionViewController: UICollectionViewController, UICollectionViewDelegateFlowLayout, PostCellDelegate {
var totalPrice = Float()
private var hiddenRows = Set<Int>()
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath) as! PostCell
cell.finalLabel.text = String(totalPrice)
cell.delegate = self
let item = itemsArr[indexPath.row]
cell.set(name: item.name, brand: item.brand, price: item.price)
if indexPath.row == itemsArr.count - 1 {
print("last item")
}else {
print("not last")
}
if hiddenRows.contains(indexPath.row) {
cell.myButton.isHidden = true
cell.removeButton.isHidden = false
}else{
cell.removeButton.isHidden = true
cell.myButton.isHidden = false
}
cell.finalLabel.text = String(totalPrice)
return cell
}
#objc func addTapped(cell: PostCell) {
guard let indexPath = self.collectionView.indexPath(for: cell) else {return}
hiddenRows.insert(indexPath.row)
cell.removeButton.isHidden = false
let item = itemsArr[indexPath.row]
print(item.price)
totalPrice += Float(item.price) ?? 0
cell.finalLabel.text = String(totalPrice)
}
#objc func removeButtonTapped(cell: PostCell) {
guard let indexPath = self.collectionView.indexPath(for: cell) else {return}
hiddenRows.insert(indexPath.row)
cell.myButton.isHidden = false
let item = itemsArr[indexPath.row]
totalPrice -= Float(item.price) ?? 0
cell.finalLabel.text = String(totalPrice)
}
}
protocol PostCellDelegate {
func removeButtonTapped(cell: PostCell)
func addTapped(cell: PostCell)
func didPressButton(_ tag: Int)
}
class PostCell: UICollectionViewCell {
var delegate: PostCellDelegate?
func set(name: String, brand: String, price: String){
nameLabel.text = name
brandLabel.text = brand
priceLabel.text = price
}
override init(frame: CGRect) {
super.init(frame: frame)
self.myButton.addTarget(self, action: #selector(addButtonTapped(sender:)), for: .touchUpInside)
self.removeButton.addTarget(self, action: #selector(subButtonTapped(sender:)), for: .touchUpInside)
setupCellConstraints()
}
#objc func buttonPressed(_ sender: UIButton) {
delegate?.didPressButton(sender.tag)
}
#objc func addButtonTapped(sender: UIButton){
self.delegate?.addTapped(cell: self)
sender.isHidden = true
}
#objc func subButtonTapped(sender: UIButton){
self.delegate?.removeButtonTapped(cell: self)
sender.isHidden = true
}
}

How can I parse each items into it's on uicollection view cell

So below is my code for parsing JSON and putting it into a collection view cell
import UIKit
let cellId = "cellId"
struct AnimeJsonStuff: Decodable {
let data: [AnimeDataArray]
}
struct AnimeLinks: Codable {
var selfStr : String?
private enum CodingKeys : String, CodingKey {
case selfStr = "self"
}
}
struct AnimeAttributes: Codable {
var createdAt : String?
var slug : String?
let synopsis: String?
private enum CodingKeys : String, CodingKey {
case createdAt = "createdAt"
case slug = "slug"
case synopsis = "synopsis"
}
}
struct AnimeRelationships: Codable {
var links : AnimeRelationshipsLinks?
private enum CodingKeys : String, CodingKey {
case links = "links"
}
}
struct AnimeRelationshipsLinks: Codable {
var selfStr : String?
var related : String?
private enum CodingKeys : String, CodingKey {
case selfStr = "self"
case related = "related"
}
}
struct AnimeDataArray: Codable {
let id: String?
let type: String?
let links: AnimeLinks?
let attributes: AnimeAttributes?
let relationships: [String: AnimeRelationships]?
private enum CodingKeys: String, CodingKey {
case id = "id"
case type = "type"
case links = "links"
case attributes = "attributes"
case relationships = "relationships"
}
}
class OsuHomeController: UICollectionViewController, UICollectionViewDelegateFlowLayout {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
navigationItem.title = "Kitsu - Your anime feed"
collectionView?.backgroundColor = UIColor(red:0.09, green:0.13, blue:0.19, alpha:1.0)
collectionView?.register(viewControllerCells.self, forCellWithReuseIdentifier: cellId)
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 3
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
return collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: 350, height: 150)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
return UIEdgeInsets(top: 15, left: 0, bottom: 10, right: 0)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
class viewControllerCells: UICollectionViewCell {
func jsonDecoding() {
let jsonUrlString = "https://kitsu.io/api/edge/anime"
guard let url = URL(string: jsonUrlString) else {return}
URLSession.shared.dataTask(with: url) { (data, response, err) in
guard let data = data else {return}
do {
let animeJsonStuff = try JSONDecoder().decode(AnimeJsonStuff.self, from: data)
for anime in animeJsonStuff.data {
// print(anime.id)
// print(anime.type)
// print(anime.links?.selfStr)
let animeName = anime.attributes?.slug
let animeSynopsis = anime.attributes?.synopsis
print(animeName)
DispatchQueue.main.async {
self.nameLabel.text = animeName
self.synopsis.text = animeSynopsis
}
for (key, value) in anime.relationships! {
// print(key)
// print(value.links?.selfStr)
// print(value.links?.related)
}
}
} catch let jsonErr {
print("Error serializing json", jsonErr)
}
}.resume()
}
let nameLabel: UILabel = {
let label = UILabel()
label.textColor = UIColor.black
return label
}()
let profileImageView: UIImageView = {
let imageView = UIImageView()
imageView.contentMode = .scaleAspectFit
return imageView
}()
let synopsis: UILabel = {
let label = UILabel()
label.textColor = UIColor.black
return label
}()
override init(frame: CGRect) {
super.init(frame: frame)
setupViews()
jsonDecoding()
self.layer.shadowOpacity = 0.05
self.layer.shadowRadius = 0.05
self.layer.cornerRadius = 1
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setupViews() {
backgroundColor = UIColor(red:0.86, green:0.87, blue:0.89, alpha:1.0)
addSubview(nameLabel.self)
addSubview(synopsis.self)
addConstraintsWithFormat("H:|-18-[v0]|", views: synopsis)
addConstraintsWithFormat("V:|-8-[v0]|", views: synopsis)
addConstraintsWithFormat("H:|-12-[v0]|", views: nameLabel)
}
}
extension UIColor {
static func rgb(_ red: CGFloat, green: CGFloat, blue: CGFloat) -> UIColor {
return UIColor(red: red/255, green: green/255, blue: blue/255, alpha: 1)
}
}
extension UIView {
func addConstraintsWithFormat(_ format: String, views: UIView...) {
var viewsDictionary = [String: UIView]()
for (index, view) in views.enumerated() {
let key = "v\(index)"
viewsDictionary[key] = view
view.translatesAutoresizingMaskIntoConstraints = false
}
addConstraints(NSLayoutConstraint.constraints(withVisualFormat: format, options: NSLayoutFormatOptions(), metrics: nil, views: viewsDictionary))
}
which works but however instead of putting in all the data like this
Optional("cowboy-bebop")
Optional("cowboy-bebop-tengoku-no-tobira")
Optional("trigun")
Optional("witch-hunter-robin")
Optional("beet-the-vandel-buster")
Optional("eyeshield-21")
Optional("honey-and-clover")
Optional("hungry-heart-wild-striker")
Optional("initial-d-fourth-stage")
Optional("monster")
Optional("cowboy-bebop")
Optional("cowboy-bebop-tengoku-no-tobira")
Optional("trigun")
Optional("witch-hunter-robin")
Optional("beet-the-vandel-buster")
Optional("eyeshield-21")
Optional("honey-and-clover")
Optional("hungry-heart-wild-striker")
Optional("initial-d-fourth-stage")
Optional("monster")
Optional("cowboy-bebop")
Optional("cowboy-bebop-tengoku-no-tobira")
Optional("trigun")
Optional("witch-hunter-robin")
Optional("beet-the-vandel-buster")
Optional("eyeshield-21")
Optional("honey-and-clover")
Optional("hungry-heart-wild-striker")
Optional("initial-d-fourth-stage")
Optional("monster")
it does it like this which is wrong it should be displaying each show from top to bottom not just the final one. I did ask this question before and they said to put it in the main view did load then return the cell for item at index path with I would guess anime.attrubites.slug which doesn't seem to be working.
Please check :
OsuHomeController
let cellId = "cellId"
struct AnimeJsonStuff: Decodable {
let data: [AnimeDataArray]
}
struct AnimeLinks: Codable {
var selfStr : String?
private enum CodingKeys : String, CodingKey {
case selfStr = "self"
}
}
struct AnimeAttributes: Codable {
var createdAt : String?
var slug : String?
let synopsis: String?
private enum CodingKeys : String, CodingKey {
case createdAt = "createdAt"
case slug = "slug"
case synopsis = "synopsis"
}
}
struct AnimeRelationships: Codable {
var links : AnimeRelationshipsLinks?
private enum CodingKeys : String, CodingKey {
case links = "links"
}
}
struct AnimeRelationshipsLinks: Codable {
var selfStr : String?
var related : String?
private enum CodingKeys : String, CodingKey {
case selfStr = "self"
case related = "related"
}
}
struct AnimeDataArray: Codable {
let id: String?
let type: String?
let links: AnimeLinks?
let attributes: AnimeAttributes?
let relationships: [String: AnimeRelationships]?
private enum CodingKeys: String, CodingKey {
case id = "id"
case type = "type"
case links = "links"
case attributes = "attributes"
case relationships = "relationships"
}
}
class ViewController: UICollectionViewController, UICollectionViewDelegateFlowLayout {
var isDataLoaded = false
var animeNames: [String] = []
var animeSynopsis: [String] = []
override func viewDidLoad() {
super.viewDidLoad()
self.jsonDecoding()
// Do any additional setup after loading the view, typically from a nib.
navigationItem.title = "Kitsu - Your anime feed"
collectionView?.backgroundColor = UIColor(red:0.09, green:0.13, blue:0.19, alpha:1.0)
collectionView?.register(viewControllerCells.self, forCellWithReuseIdentifier: cellId)
collectionView?.delegate = self
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return animeNames.count
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath) as! viewControllerCells
cell.nameLabel.text = animeNames[indexPath.row]
cell.synopsis.text = animeSynopsis[indexPath.row]
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: 350, height: 150)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
return UIEdgeInsets(top: 15, left: 0, bottom: 10, right: 0)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func jsonDecoding() {
let jsonUrlString = "https://kitsu.io/api/edge/anime"
guard let url = URL(string: jsonUrlString) else {return}
URLSession.shared.dataTask(with: url) { (data, response, err) in
guard let data = data else {return}
do {
let animeJsonStuff = try JSONDecoder().decode(AnimeJsonStuff.self, from: data)
for anime in animeJsonStuff.data {
// print(anime.id)
// print(anime.type)
// print(anime.links?.selfStr)
if let animeName = anime.attributes?.slug {
self.animeNames.append(animeName)
} else {
self.animeNames.append("-")
}
if let animeSynop = anime.attributes?.synopsis {
self.animeSynopsis.append(animeSynop)
} else {
self.animeSynopsis.append("-")
}
for (key, value) in anime.relationships! {
// print(key)
// print(value.links?.selfStr)
// print(value.links?.related)
}
}
self.isDataLoaded = true
DispatchQueue.main.async {
self.collectionView?.reloadData()
}
} catch let jsonErr {
print("Error serializing json", jsonErr)
}
}.resume()
}
}
viewControllerCells
class viewControllerCells: UICollectionViewCell {
let nameLabel: UILabel = {
let label = UILabel()
label.textColor = UIColor.black
return label
}()
let profileImageView: UIImageView = {
let imageView = UIImageView()
imageView.contentMode = .scaleAspectFit
return imageView
}()
let synopsis: UILabel = {
let label = UILabel()
label.textColor = UIColor.black
return label
}()
override init(frame: CGRect) {
super.init(frame: frame)
setupViews()
self.layer.shadowOpacity = 0.05
self.layer.shadowRadius = 0.05
self.layer.cornerRadius = 1
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setupViews() {
backgroundColor = UIColor(red:0.86, green:0.87, blue:0.89, alpha:1.0)
addSubview(nameLabel.self)
addSubview(synopsis.self)
addConstraintsWithFormat("H:|-18-[v0]|", views: synopsis)
addConstraintsWithFormat("V:|-8-[v0]|", views: synopsis)
addConstraintsWithFormat("H:|-12-[v0]|", views: nameLabel)
}
}
extension UIColor {
static func rgb(_ red: CGFloat, green: CGFloat, blue: CGFloat) -> UIColor {
return UIColor(red: red/255, green: green/255, blue: blue/255, alpha: 1)
}
}
extension UIView {
func addConstraintsWithFormat(_ format: String, views: UIView...) {
var viewsDictionary = [String: UIView]()
for (index, view) in views.enumerated() {
let key = "v\(index)"
viewsDictionary[key] = view
view.translatesAutoresizingMaskIntoConstraints = false
}
addConstraints(NSLayoutConstraint.constraints(withVisualFormat: format, options: NSLayoutFormatOptions(), metrics: nil, views: viewsDictionary))
}
}

ios: All data is loading from json web request except SliderCollectionViewCell

I have sliderCollectionViewController in UICollectionViewCell, try to loading data from json web, all data is loading without image. Here I like to load images in slideCollectionViewCell which created in a collectionViewCell.
import UIKit
import Foundation
**DescriptionObject**
`class Description: NSObject {
var id: Int?
var product_id: Int?
var myDescription: String?
var product_description: String?
var all_images: [String]?
}
**DescriptionCollectionViewController with slideCollectionViewController**
class DescriptionCollectionView: UICollectionViewController, UICollectionViewDelegateFlowLayout{
var arrDescription = [Description]()
**json request**
func loadDescription(){
ActivityIndicator.customActivityIndicatory(self.view, startAnimate: true)
let url = URL(string: ".........")
URLSession.shared.dataTask(with:url!) { (urlContent, response, error) in
if error != nil {
print(error ?? 0)
}
else {
do {
let json = try JSONSerialization.jsonObject(with: urlContent!) as! [String:Any]
let myProducts = json["products"] as? [String: Any]
let myData = myProducts?["data"] as? [[String:Any]]
myData?.forEach { dt in
let oProduct = Description()
oProduct.id = dt["id"] as? Int
oProduct.product_id = dt["product_id"] as? Int
oProduct.myDescription = dt["description"] as? String
oProduct.product_description = dt["product_description"] as? String
if let allImages = dt["all_images"] as? [[String:Any]] {
oProduct.all_images = allImages.flatMap { $0["image"] as? String }
}
self.arrDescription.append(oProduct)
}
} catch let error as NSError {
print(error)
}
}
DispatchQueue.main.async(execute: {
ActivityIndicator.customActivityIndicatory(self.view, startAnimate: false)
self.collectionView?.reloadData()
})
}.resume()
}
fileprivate let cellId = "cellId"
fileprivate let descriptionCellId = "descriptionCellId"
override func viewDidLoad() {
super.viewDidLoad()
self.loadDescription()
collectionView?.register(DescriptionCell.self, forCellWithReuseIdentifier: descriptionCellId)
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return arrDescription.count
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: descriptionCellId, for: indexPath) as! DescriptionCell
cell.descriptionOb = arrDescription[indexPath.item]
return cell
}
**DescriptionCollectionViewCell**
class DescriptionCell: UICollectionViewCell, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {
var descriptionOb: Description!{
didSet{
descriptionTextView.text = descriptionOb?.myDescription
couponTextView.text = descriptionOb?.product_description
slideCollectionView.reloadData()
}
}
override init(frame: CGRect) {
super.init(frame: frame)
setupCell()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
let descriptionTextView: UITextView = {
let textview = UITextView()
textview.text = "Description is the pattern of development "
return textview
}()
let couponTextView: UITextView = {
let textview = UITextView()
textview.text = "Description is the pattern of development "
return textview
}()
fileprivate let cellId = "cellId"
lazy var slideCollectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
cv.backgroundColor = UIColor.clear
return cv
}()
func setupCell() {
slideCollectionView.dataSource = self
slideCollectionView.delegate = self
slideCollectionView.isPagingEnabled = true
slideCollectionView.register(SlideCell.self, forCellWithReuseIdentifier: cellId)
addSubview(slideCollectionView)
addSubview(descriptionTextView)
addSubview(couponTextView)
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
if let count = descriptionOb?.all_images?.count{
return count
}
return 0
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath) as! SlideCell
if let imageName = descriptionOb?.all_images?[indexPath.item] {
cell.imageView.image = UIImage(named: imageName)
}
return cell
}
}
**SlideCollectionViewCell**
class SlideCell: UICollectionViewCell{
override init(frame: CGRect) {
super.init(frame: frame)
setupCellSlider()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
let imageView: CustomImageView = {
let iv = CustomImageView()
iv.contentMode = .scaleAspectFill
iv.image = UIImage(named: "defaultImage3")
iv.backgroundColor = UIColor.green
return iv
}()
func setupCellSlider() {
backgroundColor = .green
addSubview(imageView)
}
}`
**Image Extension**
let imageCache = NSCache<AnyObject, AnyObject>()
class CustomImageView: UIImageView {
var imageUrlString: String?
func loadImageUsingUrlString(_ urlString: String) {
imageUrlString = urlString
guard let urlEncoded = urlString.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) else {
print("Encoding not done")
return
}
let url = URL(string: urlEncoded)
image = nil
if let imageFromCache = imageCache.object(forKey: urlString as AnyObject) as? UIImage {
self.image = imageFromCache
return
}
if let url = url {
URLSession.shared.dataTask(with: url, completionHandler: {(myData, respones, error) in
if error != nil {
print(error ?? 0)
return
}
if let myData = myData {
DispatchQueue.main.async(execute: {
let imageToCache = UIImage(data: myData)
if self.imageUrlString == urlString {
self.image = imageToCache
}
if let imageToCache = imageToCache {
imageCache.setObject(imageToCache, forKey: urlString as AnyObject)
}
})
}
}).resume()
}
}
}
json web data
You should use the method in UIImageView subclass CustomImageView
so instead of
cell.imageView.image = UIImage(named: imageName)
try this:
cell.imageView.loadImageUsingUrlString(imageName)
in you cellForItem method of DescriptionCell

JSON only showing last optional in all cells Swift 4

So the code posted below is my stuct
struct AnimeJsonStuff: Decodable {
let data: [AnimeDataArray]
}
struct AnimeLinks: Codable {
var selfStr : String?
private enum CodingKeys : String, CodingKey {
case selfStr = "self"
}
}
struct AnimeAttributes: Codable {
var createdAt : String?
var slug : String?
private enum CodingKeys : String, CodingKey {
case createdAt = "createdAt"
case slug = "slug"
}
}
struct AnimeRelationships: Codable {
var links : AnimeRelationshipsLinks?
private enum CodingKeys : String, CodingKey {
case links = "links"
}
}
struct AnimeRelationshipsLinks: Codable {
var selfStr : String?
var related : String?
private enum CodingKeys : String, CodingKey {
case selfStr = "self"
case related = "related"
}
}
struct AnimeDataArray: Codable {
let id: String?
let type: String?
let links: AnimeLinks?
let attributes: AnimeAttributes?
let relationships: [String: AnimeRelationships]?
private enum CodingKeys: String, CodingKey {
case id = "id"
case type = "type"
case links = "links"
case attributes = "attributes"
case relationships = "relationships"
}
}
This code is my function for parsing data:
func jsonDecoding() {
let jsonUrlString = "https://kitsu.io/api/edge/anime"
guard let url = URL(string: jsonUrlString) else {return}
URLSession.shared.dataTask(with: url) { (data, response, err) in
guard let data = data else {return}
do {
let animeJsonStuff = try JSONDecoder().decode(AnimeJsonStuff.self, from: data)
for anime in animeJsonStuff.data {
// print(anime.id)
// print(anime.type)
// print(anime.links?.selfStr)
let animeName = anime.attributes?.slug
print(animeName)
DispatchQueue.main.async {
self.nameLabel.text = animeName
}
for (key, value) in anime.relationships! {
// print(key)
// print(value.links?.selfStr)
// print(value.links?.related)
}
}
} catch let jsonErr {
print("Error serializing json", jsonErr)
}
}.resume()
}
This is what the console prints out:
Optional("cowboy-bebop")
Optional("cowboy-bebop-tengoku-no-tobira")
Optional("trigun")
Optional("witch-hunter-robin")
Optional("beet-the-vandel-buster")
Optional("eyeshield-21")
Optional("honey-and-clover")
Optional("hungry-heart-wild-striker")
Optional("initial-d-fourth-stage")
Optional("monster")
Optional("cowboy-bebop")
Optional("cowboy-bebop-tengoku-no-tobira")
Optional("trigun")
Optional("witch-hunter-robin")
Optional("beet-the-vandel-buster")
Optional("eyeshield-21")
Optional("honey-and-clover")
Optional("hungry-heart-wild-striker")
Optional("initial-d-fourth-stage")
Optional("monster")
Optional("cowboy-bebop")
Optional("cowboy-bebop-tengoku-no-tobira")
Optional("trigun")
Optional("witch-hunter-robin")
Optional("beet-the-vandel-buster")
Optional("eyeshield-21")
Optional("honey-and-clover")
Optional("hungry-heart-wild-striker")
Optional("initial-d-fourth-stage")
Optional("monster")
It now displays the text but it only displays the last optional called monster and not all the other ones when I have three cells. It only displays monster in each cell.
It should be
1st cell: Cowboy-bebpop
2nd cell: cowboy-bebop-tengoku-no-tobira
3rd cell: trigun
and etc
Table view methods:
let nameLabel: UILabel = {
let label = UILabel()
label.textColor = UIColor.black
return label
}()
let profileImageView: UIImageView = {
let imageView = UIImageView()
imageView.contentMode = .scaleAspectFit
return imageView
}()
let synopsis: UILabel = {
let label = UILabel()
label.textColor = UIColor.black
return label
}()
override init(frame: CGRect) {
super.init(frame: frame)
setupViews()
jsonDecoding()
self.layer.shadowOpacity = 0.05
self.layer.shadowRadius = 0.05
self.layer.cornerRadius = 1
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setupViews() {
backgroundColor = UIColor(red:0.86, green:0.87, blue:0.89, alpha:1.0)
addSubview(nameLabel.self)
addSubview(synopsis.self)
addConstraintsWithFormat("H:|-18-[v0]|", views: synopsis)
addConstraintsWithFormat("V:|-8-[v0]|", views: synopsis)
addConstraintsWithFormat("H:|-12-[v0]|", views: nameLabel)
}
}
extension UIColor {
static func rgb(_ red: CGFloat, green: CGFloat, blue: CGFloat) -> UIColor {
return UIColor(red: red/255, green: green/255, blue: blue/255, alpha: 1)
}
}
extension UIView {
func addConstraintsWithFormat(_ format: String, views: UIView...) {
var viewsDictionary = [String: UIView]()
for (index, view) in views.enumerated() {
let key = "v\(index)"
viewsDictionary[key] = view
view.translatesAutoresizingMaskIntoConstraints = false
}
addConstraints(NSLayoutConstraint.constraints(withVisualFormat: format, options: NSLayoutFormatOptions(), metrics: nil, views: viewsDictionary))
}
}
Cell functions:
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
navigationItem.title = "Kitsu - Your anime feed"
collectionView?.backgroundColor = UIColor(red:0.09, green:0.13, blue:0.19, alpha:1.0)
collectionView?.register(viewControllerCells.self, forCellWithReuseIdentifier: cellId)
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 3
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
return collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: 350, height: 150)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
return UIEdgeInsets(top: 15, left: 0, bottom: 10, right: 0)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
If your jsonDecoding() func is placed in each cell, it will fetch all items, then in the for cycle inside, it will cycle through each fetched item changing the label from first to the last. Once reached the last the label will never change anymore.

Resources