Cannot display title(String) and image from array to CollectionView in swift 4 - arrays

I have a collection view and array with URLs of different images. I would like to display titles and images in the collection view. But I can't display and there is no error message found.
How can achieve it? In the console, all results can show. I have no idea how to do it.
import UIKit
import Foundation
import SwiftyJSON
class MainPageController: UIViewController, UICollectionViewDelegate,UICollectionViewDataSource{
public var foodImage = [UIImageView]()
public var foodTitle = [String]()
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
collectionView.dataSource = self
collectionView.delegate = self
return foodTitle.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! MainPageCollectionViewCell
cell.FoodTitle.text = self.foodTitle[indexPath.item]
//cell.Food.image = foodImage[indexPath.item] as? UIImage
return cell
}
var fullScreenSize :CGSize!
#IBOutlet weak var CollectionView: UICollectionView!
#IBOutlet weak var DisplayDateAndTime: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
//retrieve screen size
fullScreenSize =
UIScreen.main.bounds.size
// setup backgroud color
self.view.backgroundColor =
UIColor.white
fetchFoodList()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func getCurrentDateTime(){
let formatter = DateFormatter()
formatter.dateStyle = .long
//formatter.timeStyle = .medium
let str = formatter.string(from: Date())
DisplayDateAndTime?.text = str
}
}
extension MainPageController{
public func fetchFoodList(){
let url = URL(string: SomeUrlString)
let task = URLSession.shared.dataTask(with: url!){ (data, response, error) in
if error != nil{
print(error!)
} else {
if let urlContent = data{
do {
let json = try JSON(data:data!)
let recipes = json["hits"]
self.foodTitle = json["hits"].arrayValue.map {$0["recipe"]["label"].stringValue}
print(self.foodTitle)
var foodImage = json["hits"].arrayValue.map {$0["recipe"]["image"].stringValue}
print(foodImage)
print(self.foodImage)
}
catch{
print("JSON Processing Failed")
}
}
}
task.resume()
}
}
Here is the result in the console:
["Chicken Vesuvio", "Chicken Paprikash", "Chicken Gravy", "Catalan Chicken", "Persian Chicken", "Kreplach (Chicken Dumplings)", "Dijon Chicken", "Roast Chicken", "Chicken cacciatore", "Tarragon Chicken"]
["https://www.edamam.com/web-img/e42/e42f9119813e890af34c259785ae1cfb.jpg", "https://www.edamam.com/web-img/e12/e12b8c5581226d7639168f41d126f2ff.jpg", "https://www.edamam.com/web-img/fd1/fd1afed1849c44f5185720394e363b4e.jpg", "https://www.edamam.com/web-img/4d9/4d9084cbc170789caa9e997108b595de.jpg", "https://www.edamam.com/web-img/8f8/8f810dfe198fa3e520d291f3fcf62bbf.jpg"]

You have to set collectionView's datasource and delegate into your viewController's viewDidLoad not in (collectionView:numberOfItemsInSection:)
override func viewDidLoad() {
super.viewDidLoad()
collectionView.dataSource = self
collectionView.delegate = self
//retrieve screen size
fullScreenSize = UIScreen.main.bounds.size
// setup backgroud color
self.view.backgroundColor = UIColor.white
fetchFoodList()
}

You are trying to set your collectionView's delegate and datasource in a dataSource function (collectionView:numberOfItemsInSection:) which can not work.
Instead set the delegate and datasource in your viewController's viewDidLoad or since you are using storyboard directly in the interface builder.
override func viewDidLoad() {
super.viewDidLoad()
collectionView.delegate = self
collectionView.dataSource = self
//retrieve screen size
fullScreenSize = UIScreen.main.bounds.size
// setup backgroud color
self.view.backgroundColor = UIColor.white
fetchFoodList()
}
Make also sure to call collectionView.reloadData() in the completion block of fetchFoodList().
func fetchFoodList() {
let url = URL(string: SomeUrlString)
let task = URLSession.shared.dataTask(with: url!){ (data, response, error) in
if error != nil{
print(error!)
} else {
if let urlContent = data{
do {
let json = try JSON(data:data!)
let recipes = json["hits"]
self.foodTitle = json["hits"].arrayValue.map {$0["recipe"]["label"].stringValue}
print(self.foodTitle)
var foodImage = json["hits"].arrayValue.map {$0["recipe"]["image"].stringValue}
print(foodImage)
print(self.foodImage)
DispatchQueue.main.async {
self.collectionView.reloadData()
}
}
catch{
print("JSON Processing Failed")
}
}
}
task.resume()
}

Related

Index out of range for cell text

I am making a shopping app but I've come to a problem.
So far I've been json parsing, making a the tableview cell but I've come to an error where it says out of range:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "hello", for: indexPath) as! DisplayShopTableCellTableViewCell
// Configure the cell...
cell.userLbl.text = namee[indexPath.row] //Index out of range
cell.passLbl.text = myprice[indexPath.row]
let imagedata = try! Data(contentsOf: mySecond[indexPath.row].imageUrl!)
cell.tablecellimageview.image = UIImage(data: imagedata)
return cell
}
This is my display shop table cell:
class DisplayShopTableCellTableViewCell: UITableViewCell {
#IBOutlet weak var userLbl: UILabel!
#IBOutlet weak var passLbl: UILabel!
#IBOutlet weak var tablecellimageview: UIImageView!
Parsing
func extracted(){
guard let url = URL(string: "http://rajeshrmohan.com/sport.json")
else {
return
}
let task = URLSession.shared.dataTask(with: url){
(data,response,error) in
guard let dataResponse = data,
error == nil else {
print(error?.localizedDescription ?? "Response Error")
return
}
do {
let decoder = JSONDecoder()
let model = try decoder.decode(FullClothes.self, from: dataResponse)
//print(model)
for i in 0..<model.item.count{
self.namee.append(model.item[i].name!)
self.myprice.append(String(model.item[i].price!))
self.myphoto.append(model.item[i].image!)
}
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
catch let parsingError {
print("Error", parsingError)
}
}
task.resume()
}
I think the parsing is done in a wrong way, additionally, as El Tomato commented, why using multiple arrays for constructing a cell, one more comment, configuring cell shouldn't be done inside cellForRowAt, it would be better done within cell itself.
Check the following code snippet and let me know if you need any help withh it.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "hello", for: indexPath) as! DisplayShopTableCellTableViewCell
// Configure the cell...
cell.bind(self.myData[indexPath.row])
return cell
}
class DisplayShopTableCellTableViewCell: UITableViewCell {
#IBOutlet weak var userLbl: UILabel!
#IBOutlet weak var passLbl: UILabel!
#IBOutlet weak var tablecellimageview: UIImageView!
func bind(_ cellData: Item) {
self.userLbl.text = cellData.name
self.passLbl.text = String(cellData.price)
guard let imageURL = URL(string: "\(cellData.image)") else {
preconditionFailure("Invalid static URL string: \(cellData.image)")
}
guard let imagedata = try? Data(contentsOf: imageURL) else {
return
}
self.tablecellimageview.image = UIImage(data: imagedata)
}
}
struct Item: Decodable {
let name: String
let price: Float
let image: String
}
func extracted() {
guard let url = URL(string: "http://rajeshrmohan.com/sport.json")
else {
return
}
let task = URLSession.shared.dataTask(with: url){
(data,response,error) in
guard let dataResponse = data,
error == nil else {
print(error?.localizedDescription ?? "Response Error")
return
}
do {
let decoder = JSONDecoder()
let items: [Item] = try decoder.decode([Item].self, from: dataResponse)
//print(model)
for i in 0..<items.count {
self.myData.append(items[i])
}
}
catch let parsingError {
print("Error", parsingError)
}
}
task.resume()
}
Finally, try as far as you can to avoid force casting, check this question for further insights about force casting.
Optional chaining would be helpful too.

Fatal error: unexpected found nil while unwrapping an Optional value

I have a problem with an array of data, in this data array I send the name of 6 images so that later they are loaded in a CollectionView, the 6 images load well, without any problem, but when I add a String value to send it gives me a error that is empty:
This is my class where is my data array:
import UIKit
class HBook{
var imagenB: UIImage!
var estatus: String!
init(estatus: String, imagenB: UIImage) {
self.estatus = estatus
self.imagenB = imagenB
}
class func getData() -> [HBook]{
let rawData = [
["imagenB":"book1"],
["imagenB":"book2"],
["imagenB":"book3"],
["imagenB":"book4"],
["imagenB":"book5"],
["imagenB":"book6"],
["estatus":"No reservado"]
]
var hbook:[HBook] = []
for item in rawData{
hbook.append(HBook(estatus: item["estatus"]!, imagenB: UIImage(named: item["imagenB"]!)!))
}
return hbook
}
}
I print my data array to see which variable is empty, but apparently all have an assigned value:
I do not know why I'm sending an empty value.
This information was retrieved in another class that has a CollectionView and a Label, the method where I passed the information is in the cellForItemAt method:
class DetailViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
#IBOutlet weak var contenedorCollection: UIView!
#IBOutlet weak var myCollection: UICollectionView!
#IBOutlet weak var pages: UIPageControl!
#IBOutlet weak var estatus: UILabel!
var hbook = HBook.getData()
var nombreH = ""
override func viewDidLoad() {
super.viewDidLoad()
pages.numberOfPages = hbook.count
self.title = nombreH
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return hbook.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! CellCollectionViewCell
cell.imageview.image = hbook[indexPath.row].imagenB
estatus.text = hbook[indexPath.row].estatus
return cell
}
I think the problem is in data class. Please replace your HBook class with following code and it will work without any further change :
class HBook{
var imagenB: UIImage?
var estatus: String?
init(estatus: String? = nil, imagenB: UIImage? = nil) {
self.estatus = estatus
self.imagenB = imagenB
}
class func getData() -> [HBook]{
let rawData = [
["imagenB":"book1","estatus":"No reservado"],
["imagenB":"book2","estatus":"No reservado"],
["imagenB":"book3","estatus":"No reservado"],
["imagenB":"book4","estatus":"No reservado"],
["imagenB":"book5","estatus":"No reservado"],
["imagenB":"book6","estatus":"No reservado"]
]
var hbook:[HBook] = []
for item in rawData{
if let image = item["itemnB"]{
hbook.append(HBook(estatus: item["estatus"], imagenB:UIImage(named:image)))
}else{
hbook.append(HBook(estatus: item["estatus"]))
}
}
return hbook
}
}

URL array throwing optional error swift

I am not sure how to resolve the optional type error that occurs at "if let imageURL = imageFile.path" in my code below. The error it throws is "Initializer for conditional binding must have Optional type, not 'String'"
After googling, I'm guessing it has something to do with the directoryContentsArray = URL I set at the beginning of my CollectionViewController class.
Please help!
P.S. Sorry for the repeat optional error question, but I'm super confused :/
class CollectionViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
var directoryContentsArray = [URL]()
fileprivate let itemsPerRow: CGFloat = 3
fileprivate let sectionInsets = UIEdgeInsets(top: 50.0, left: 20.0, bottom: 50.0, right: 20.0)
#IBOutlet weak var collectionView: UICollectionView! { didSet {
collectionView.delegate = self
collectionView.dataSource = self
}
}
override func viewDidLoad() {
super.viewDidLoad()
func fetchDirectoryContents() {
let fileManager = FileManager.default
let documentDirectory = fileManager.urls(for: .documentDirectory, in: .userDomainMask)[0]
let directoryContents = try! fileManager.contentsOfDirectory(at: documentDirectory, includingPropertiesForKeys: nil)
self.directoryContentsArray = directoryContents
self.collectionView.reloadData()
}
checkPhotoLibraryPermission()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
self.collectionView.reloadData()
}
//number of views
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return directoryContentsArray.count
}
//populate the views
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as? myCell {
let imageFile = self.directoryContentsArray[indexPath.item]
if let imageURL = imageFile.path,
imageFile.pathExtension == "jpeg",
let image = UIImage(contentsOfFile: imageURL) {
cell.myImageView.image = image
} else {
fatalError("Can't create image from file \(imageFile)")
}
return cell
}
return UICollectionViewCell()
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
if let imageURL = info[UIImagePickerControllerImageURL] as? URL {
let documentDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
do {
try FileManager.default.moveItem(at: imageURL.standardizedFileURL, to: documentDirectory.appendingPathComponent(imageURL.lastPathComponent))
collectionView.reloadData()
} catch {
print(error)
}
}
picker.dismiss(animated: true, completion: nil)
}
Thanks again!
The definition of the path property of URL is:
var path: String
So it doesn't return an optional which means you don't need to do the let assignment.
Just change to this:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as? myCell {
let imageFile = self.directoryContentsArray[indexPath.item]
if imageFile.pathExtension == "jpeg",
let image = UIImage(contentsOfFile: imageFile.path) {
cell.myImageView.image = image
} else {
fatalError("Can't create image from file \(imageFile)")
}
return cell
}
return UICollectionViewCell()
}

Getting JSON data to populate a TableView from an Array

I'm able to successfully populate the array with my JSON data inside of the loop, and I'm trying to populate the cells of my TableView with the same information, but currently the list comes up with no content.
import UIKit
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var table: UITableView!
var headlines = [String]()
let baseURL = "http://api.nytimes.com/svc/topstories/v1/business.json?api-key=123456789"
override func viewDidLoad() {
getJSON()
super.viewDidLoad()
self.table.registerClass(UITableViewCell.self, forCellReuseIdentifier: "cell")
self.table.dataSource = self
self.table.delegate = self
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func getJSON() {
let url = NSURL(string: baseURL)
let request = NSURLRequest(URL: url!)
let session = NSURLSession(configuration: NSURLSessionConfiguration.defaultSessionConfiguration())
let task = session.dataTaskWithRequest(request){ (data, response, error) -> Void in
if error == nil {
let SwiftyJSON = JSON(data: data!)
let theTitle = SwiftyJSON["results"].arrayValue
for title in theTitle{
let titles = title["title"].stringValue
self.headlines.insert(titles, atIndex: 0)
//print("- " + titles)
}
print(self.headlines)
}
else {
print("there was an error")
}
}
task.resume()
}
// From the UITAbleViewDataSource
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return headlines.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = self.table.dequeueReusableCellWithIdentifier("cell")! as UITableViewCell
cell.textLabel!.text = self.headlines[indexPath.row]
return cell
}
// From the UITableViewDelegate
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
print("You tapped on cell # \(indexPath.row)")
}
}
The task is asynchronous, so when you've loaded the array you have to reload your table
func getJSON() {
let url = NSURL(string: baseURL)
let request = NSURLRequest(URL: url!)
let session = NSURLSession(configuration: NSURLSessionConfiguration.defaultSessionConfiguration())
let task = session.dataTaskWithRequest(request){ (data, response, error) -> Void in
if error == nil {
let SwiftyJSON = JSON(data: data!)
let theTitle = SwiftyJSON["results"].arrayValue
for title in theTitle{
let titles = title["title"].stringValue
self.headlines.insert(titles, atIndex: 0)
//print("- " + titles)
}
print(self.headlines)
self.table.reloadData()
}
else {
print("there was an error")
}
}
task.resume()
}

Fatal error: Array index out of range. About a collectionView

The controller has a collectionView, including 1 cell, 5 section and some row, downloading data from LeanCloud just like Parse. Code always fails with fatal error: Array index out of range. In my opinion, I may have some problem in dealing with array of array, about how to access and how to add element. Any one can help me solve this bug? The bug line is listed below:
var temp = self.restaurantLean[number].
import UIKit
import AVOSCloud
class DiscoverViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource, RestaurantLeanCollectionCellDelegate, UIGestureRecognizerDelegate {
#IBOutlet var imageView: UIImageView!
#IBOutlet var collectionView: UICollectionView!
private var restaurantLean = [[RestaurantLean]]()
override func viewDidLoad() {
super.viewDidLoad()
collectionView.backgroundColor = UIColor.clearColor()
loadTripsFromLeanCloud()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
//MARK: Data Source
func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
return restaurantLean.count
}
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return restaurantLean[section].count
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("Cell", forIndexPath: indexPath) as! RestaurantLeanCollectionCell
cell.delegate = self
cell.nameLabel.text = restaurantLean[indexPath.section][indexPath.row].name
cell.typeLabel.text = restaurantLean[indexPath.section][indexPath.row].type
cell.locationLabel.text = restaurantLean[indexPath.section][indexPath.row].location
cell.isLike = restaurantLean[indexPath.section][indexPath.row].isLike
cell.imageView.image = UIImage()
cell.layer.cornerRadius = 4.0
if let image = restaurantLean[indexPath.section][indexPath.row].image {
image.getDataInBackgroundWithBlock({ (imageData, error) -> Void in
print(image)
if let data = imageData {
print("loading")
cell.imageView.image = UIImage(data: data)
print("success")
}
})
}
return cell
}
//Download the data from Baas LeanCloud
func loadTripsFromLeanCloud() {
restaurantLean.removeAll(keepCapacity: true)
for number in 0...4 {
let name = "Restaurant_" + String(number)
print(name)
print(number)
let query = AVQuery(className: name)
query.cachePolicy = AVCachePolicy.NetworkElseCache
print("1")
query.findObjectsInBackgroundWithBlock({ (objects, error) -> Void in
print("2")
if let error = error {
print("3")
print("Error: \(error) \(error.userInfo)")
}
print("4")
if let objects = objects {
print("5")
for (index, object) in objects.enumerate() {
let restaurant = RestaurantLean(avObject: object as! AVObject)
self.restaurantLean[number].append(restaurant)
let indexPath = NSIndexPath(forRow: index, inSection: number)
self.collectionView.insertItemsAtIndexPaths([indexPath])
}
}
})
print("6")
}
}
You are not adding elements to restaurantLean array itself (you only add objects to nested arrays). Here is possible solution.
func loadTripsFromLeanCloud() {
restaurantLean.removeAll(keepCapacity: true)
for number in 0...4 {
restaurantLean.append([]) // This line
// ...
}
}

Resources