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
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()
}
}
The problem is that the text labels are missing on some cells and when I click the like button, the text label is supposed to update the label but instead, the whole collection view disappears. The text labels are fetching the numbers from firebase.
Here is the relevant code:
HomeController.swift:
class HomeController: UICollectionViewController, UICollectionViewDelegateFlowLayout, HomePostCellDelegate {
var hpc: HomePostCell!
let cellId = "cellId"
override func viewDidLoad() {
super.viewDidLoad()
collectionView?.register(HomePostCell.self, forCellWithReuseIdentifier: cellId)
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath) as! HomePostCell
self.hpc = cell
cell.post = posts[indexPath.item]
cell.delegate = self
return cell
}
#objc func reload(likesLabelNotification: Notification) {
guard let likesLabel = likesLabelNotification.userInfo?["likesLabelInfo"] as? UILabel else { return }
guard let indexPath = collectionView?.indexPath(for: hpc) else { return }
let post = self.posts[indexPath.item]
guard let postId = post.id else { return }
let postUserId = post.user.uid
let numOfLikesRef = FIRDatabase.database().reference().child("likes").child(postId)
numOfLikesRef.observe(.value, with: { (snapshot: FIRDataSnapshot!) in
likesLabel.isHidden = false
let numOfChildrens = snapshot.childrenCount
likesLabel.text = "\(numOfChildrens)"
}, withCancel: { (error) in
print("failed to fetch num of posts: ",error)
})
self.posts[indexPath.item] = post
self.collectionView?.reloadData()
}
func didPressShareButton(for cell: HomePostCell) {
guard let indexPath = collectionView?.indexPath(for: cell) else { return }
let post = self.posts[indexPath.item]
guard let url = NSURL(string: post.videoUrl) else { return }
let activityViewController = UIActivityViewController(
activityItems: ["Check out this video I found on Vlogger: \(url)"],applicationActivities: nil)
present(activityViewController, animated: true, completion: nil)
}
func didLike(for cell: HomePostCell) {
guard let indexPath = collectionView?.indexPath(for: cell) else { return }
var post = self.posts[indexPath.item]
guard let postId = post.id else { return }
guard let uid = FIRAuth.auth()?.currentUser?.uid else { return }
let values = [uid : post.hasLiked == true ? 0 : 1]
FIRDatabase.database().reference().child("likes").child(postId).updateChildValues(values) { (err, _) in
if let err = err {
print("Failed to like post", err)
return
}
post.hasLiked = !post.hasLiked
self.posts[indexPath.item] = post
self.collectionView?.reloadData()
}
}
HomePostCell.swift:
protocol HomePostCellDelegate {
func didLike(for cell: HomePostCell)
}
class HomePostCell: UICollectionViewCell {
var delegate: HomePostCellDelegate?
lazy var likeButton: UIButton = {
let button = UIButton(type: .system)
button.setImage(#imageLiteral(resourceName: "heart_unselected").withRenderingMode(.alwaysOriginal), for: .normal)
button.addTarget(self, action: #selector(handleLike), for: .touchUpInside)
return button
}()
#objc func handleLike() {
delegate?.didLike(for: self)
}
lazy var likesLabel: UILabel = {
let label = UILabel()
label.font = UIFont(name: "AvenirNext-Regular", size: 20)
label.textColor = UIColor.black
label.isHidden = true
let userInfo = ["likesLabelInfo": label]
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "refresh"), object: nil, userInfo: userInfo)
return label
}()
override init(frame: CGRect) {
super.init(frame: frame)
}
static let homePostCellNotificationName = NSNotification.Name(rawValue: "homePostCellRaw")
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Please comment below if you have any questions!
Thanks in advance!
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 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.
I am following the Ray Wenderlich tutorial on Alamofire (and struggling), http://www.raywenderlich.com/85080/beginning-alamofire-tutorial#comments , and I am getting the error in the title, in the "loading more photos" part.
The error is on the line "let photoInfos = ((JSON as! NSDictionary).valueForKey("photos") as! [NSDictionary]).filter ({"
Here is my code.
//
// PhotoBrowserCollectionViewController.swift
// Photomania
//
// Created by Essan Parto on 2014-08-20.
// Copyright (c) 2014 Essan Parto. All rights reserved.
//
import UIKit
import Alamofire
class PhotoBrowserCollectionViewController: UICollectionViewController, UICollectionViewDelegateFlowLayout {
var photos = NSMutableArray()
#IBOutlet weak var PhotoBrowserCell: PhotoBrowserCollectionViewCell!
let refreshControl = UIRefreshControl()
var populatingPhotos = false
var currentPage = 1
let PhotoBrowserCellIdentifier = "PhotoBrowserCell"
let PhotoBrowserFooterViewIdentifier = "PhotoBrowserFooterView"
// MARK: Life-cycle
override func viewDidLoad() {
super.viewDidLoad()
setupView()
populatePhotos()
//// Alamofire.request(.GET, "https://api.500px.com/v1/photos").responseJSON() {
//// (_, _, data) in
//// print(data.value)}
//
// Alamofire.request(.GET, "https://api.500px.com/v1/photos", parameters: ["consumer_key": "Jj0vPllqD0zvxttgFZ1aTbRF5zy9g1yDcsTxJRFV"]).responseJSON() {
// (_,_,JSON) in
// print(JSON.value)
//
// let photoInfos = (JSON.value!.valueForKey("photos") as! [NSDictionary]).filter({
// ($0["nsfw"] as! Bool) == false
// }).map {
// PhotoInfo(id: $0["id"] as! Int, url: $0["image_url"] as! String)
// }
//
// self.photos.addObject(photoInfos)
// print(self.photos)
//
// self.collectionView?.reloadData()
// }
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
// MARK: CollectionView
override func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return photos.count
}
override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(PhotoBrowserCellIdentifier, forIndexPath: indexPath) as! PhotoBrowserCollectionViewCell
// let imageURL = (photos.objectAtIndex(indexPath.row) as! PhotoInfo).url
let imageURL = (photos.objectAtIndex(indexPath.row) as! PhotoInfo).url
Alamofire.request(.GET, imageURL).response() {
(_,_, data, _) in
let image = UIImage(data: data!)
cell.imageView.image = image
}
return cell
}
override func collectionView(collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, atIndexPath indexPath: NSIndexPath) -> UICollectionReusableView {
return collectionView.dequeueReusableSupplementaryViewOfKind(kind, withReuseIdentifier: PhotoBrowserFooterViewIdentifier, forIndexPath: indexPath)
}
override func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
performSegueWithIdentifier("ShowPhoto", sender: (self.photos.objectAtIndex(indexPath.item) as! PhotoInfo).id)
}
// MARK: Helper
func setupView() {
navigationController?.setNavigationBarHidden(false, animated: true)
let layout = UICollectionViewFlowLayout()
let itemWidth = (view.bounds.size.width - 2) / 3
layout.itemSize = CGSize(width: itemWidth, height: itemWidth)
layout.minimumInteritemSpacing = 1.0
layout.minimumLineSpacing = 1.0
layout.footerReferenceSize = CGSize(width: collectionView!.bounds.size.width, height: 100.0)
collectionView!.collectionViewLayout = layout
navigationItem.title = "Featured"
collectionView!.registerClass(PhotoBrowserCollectionViewCell.classForCoder(), forCellWithReuseIdentifier: PhotoBrowserCellIdentifier)
collectionView!.registerClass(PhotoBrowserCollectionViewLoadingCell.classForCoder(), forSupplementaryViewOfKind: UICollectionElementKindSectionFooter, withReuseIdentifier: PhotoBrowserFooterViewIdentifier)
refreshControl.tintColor = UIColor.whiteColor()
refreshControl.addTarget(self, action: "handleRefresh", forControlEvents: .ValueChanged)
collectionView!.addSubview(refreshControl)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "ShowPhoto" {
(segue.destinationViewController as! PhotoViewerViewController).photoID = sender!.integerValue
(segue.destinationViewController as! PhotoViewerViewController).hidesBottomBarWhenPushed = true
}
}
// 1
override func scrollViewDidScroll(scrollView: UIScrollView) {
if scrollView.contentOffset.y + view.frame.size.height > scrollView.contentSize.height * 0.8 {
populatePhotos()
}
}
func populatePhotos() {
// 2
if populatingPhotos {
return
}
populatingPhotos = true
// 3
Alamofire.request(Five100px.Router.PopularPhotos(self.currentPage)).responseJSON() {
(_, _, JSON) in
// 4
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)) {
// 5, 6, 7
let photoInfos = ((JSON as! NSDictionary).valueForKey("photos") as! [NSDictionary]).filter ({
($0["nsfw"] as! Bool) == false }).map { PhotoInfo(id: $0["id"] as! Int, url: $0["image_url"] as! String)}
//8
let lastItem = self.photos.count
//9
self.photos.addObject(photoInfos)
//10
let indexPaths = (lastItem..<self.photos.count).map { NSIndexPath(forItem: $0, inSection: $0) }
// 11
dispatch_async(dispatch_get_main_queue()) {
self.collectionView!.insertItemsAtIndexPaths(indexPaths)
}
self.currentPage++
}
self.populatingPhotos = false
}
}
func handleRefresh() {
}
}
class PhotoBrowserCollectionViewCell: UICollectionViewCell {
let imageView = UIImageView()
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = UIColor(white: 0.1, alpha: 1.0)
imageView.frame = bounds
addSubview(imageView)
}
}
class PhotoBrowserCollectionViewLoadingCell: UICollectionReusableView {
let spinner = UIActivityIndicatorView(activityIndicatorStyle: UIActivityIndicatorViewStyle.WhiteLarge)
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override init(frame: CGRect) {
super.init(frame: frame)
spinner.startAnimating()
spinner.center = self.center
addSubview(spinner)
}
}
the answer is very simple. On the Five100px.swift file , you will see static let consumerKey. please update consumerKey. If you live to continue same problem. please download new version https://www.dropbox.com/s/e2unowffetv9h9c/Photomania.zip?dl=0 from this link.