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.
Related
I'd like to show the CollectionView inside the B ViewController using the A ViewController's button. Image.
The information in the image is in the json file.
Gets the information and invokes the image in the Asset file.
There's no problem getting the data in json.
But nothing appears. Using the buttons in the A ViewController,
What's the problem?
The bottom is my code.
Thank you.
A ViewController
//MARK: 4. #objc Button Action
#objc func topHand(){
let cham = ChampViewViewController()
cham.modalPresentationStyle = .fullScreen
present(cham, animated: true, completion: nil)
}
B ViewController
class ChampViewViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource{
var nameArrayCount = 0
var nameArray = [String]()
private var collectionView : UICollectionView?
override func viewDidLoad() {
super.viewDidLoad()
let indenti = "top"
let layout = UICollectionViewLayout()
getJson(line: indenti)
collectionView = UICollectionView(frame: .zero,collectionViewLayout: layout)
collectionView?.delegate = self
collectionView?.dataSource = self
collectionView?.register(ChamCellCollectionViewCell.self, forCellWithReuseIdentifier: ChamCellCollectionViewCell.identifier)
guard let collectionsView = collectionView else { return }
view.addSubview(collectionsView)
collectionsView.frame = view.bounds
}
private func getJson(line:String){
let cellUrl = Bundle.main.url(forResource: line, withExtension: "json")
let cellData = NSData(contentsOf: cellUrl!)
do {
let modelJson = try JSONSerialization.jsonObject(with: cellData! as Data, options: .allowFragments ) as! NSArray
var models : [Model] = []
modelJson.forEach { json in
guard let dic = json as? [String : AnyObject] else {
return
}
let newModel = Model(name: dic["이름"] as! String,
line: dic["주라인"] as! String, type:
dic["성향"] as! String,
hp: dic["체력"] as! Int,
hpRe: dic["추가체력"] as! Int,
attackPower: dic["공격력"] as! Double,
attackPowerRe: dic["추가공격력"] as! Double,
attackSpeed: dic["공속"] as! Double,
attackSpeedRe: dic["추가공속"] as! Double,
defensive: dic["방어력"] as! Double,
defensiveRe: dic["추가방어력"] as! Double,
magicDefensive: dic["마저"] as! Double,
magicDefensiveRe: dic["추가마저"] as! Double,
row: dic["row"] as! Int,
column: dic["column"] as! Int)
models.append(newModel)
}
for data in models {
let key = data.name
nameArray.append(key)
}
nameArrayCount = nameArray.count
}catch {
fatalError()
}
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return nameArrayCount
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: ChamCellCollectionViewCell.identifier, for: indexPath) as? ChamCellCollectionViewCell else {
fatalError()
}
cell.imageConfigure(with: UIImage(named:"가렌"))
return cell
}
}
B ViewController CollectionViewCell Class
import UIKit
class ChamCellCollectionViewCell : UICollectionViewCell{
static let identifier = "ChamCellCollectionViewCell"
private let imageView : UIImageView = {
let image = UIImageView()
image.contentMode = .scaleAspectFit
return image
}()
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func imageConfigure(with image : UIImage?) {
imageView.image = image
}
override func layoutSubviews() {
super.layoutSubviews()
imageView.frame = contentView.bounds
}
override func prepareForReuse() {
super.prepareForReuse()
imageView.image = nil
}
}
Add this code to getJson() before the catch block:
DispatchQueue.main.async { [weak self] in
self?.collectionView?.reloadData()
}
Try with a didSet for your nameArrayCount.
var nameArrayCount = 0{
didSet{
collectionView.reloadData()
}
}
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.
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))
}
}
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
i put a sliderCollectionViewController in UICollectionViewCell, now everything is loading from web properly without image. but i am not getting any message about error
import UIKit
import Foundation
NSObject
class Description: NSObject {
var id: Int?
var product_id: Int?
var myDescription: String?
var all_images: [String]?
var product_description: String?
}
DescriptionCollectionViewController
class DescriptionCollectionView: UICollectionViewController, UICollectionViewDelegateFlowLayout{
var arrDescription = [Description]()
** Networking Request api **
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
oProduct.all_images = dt["all_images"] 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()
}
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
}
}
override init(frame: CGRect) {
super.init(frame: frame)
setupCell()
}
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
}()
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
}()
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
}
}
SliderCell
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)
}
}
json web
The problem is that you did:
oProduct.all_images = dt["all_images"] as? [String]
but all_images is not a string, it is a dictionary as you can see in your json.
You have to access the key image of all_images in order to show it properly.
See this:
How to access deeply nested dictionaries in Swift
You can try this:
oProduct.all_images = dt["all_images"]["image"] as? [String]
The value for key all_images is an array of dictionaries. If you want to extract all image values use the flatMap function
if let allImages = dt["all_images"] as? [[String:Any]] {
oProduct.all_images = allImages.flatMap { $0["image"] as? String }
}
But consider that the value for key image is the string representation of an URL.