I am trying to dynamically update a uicollectionview. I used this amazing tutorial on how to create a simple uicollection.
It works great when using a static array of items. My issue - I would like to have the uicollection populate with data I parsed into a new array from my db. I am not sure how to reload the uicollection after parsing my json data.
UPDATED CODE WITH ANSWER:
import UIKit
class Books: UIViewController, UICollectionViewDelegate {
#IBOutlet weak var bookscollection: UICollectionView!
var user_id: Int = 0;
//------------ init ---------------//
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidAppear(_ animated: Bool) {
showtutorial()
getuserid()
}
//------------ show books ---------------//
var booknames = [String]()
var bookcolor = [String]()
var bookdescription = [String]()
var bookid = [Int]()
func posttoapi(){
//show loading
LoadingOverlay.shared.showOverlay(view: self.view)
//send
let url:URL = URL(string: "http://www.url.com")!
let session = URLSession.shared
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.cachePolicy = NSURLRequest.CachePolicy.reloadIgnoringCacheData
let paramString = "user_id=\(user_id)"
request.httpBody = paramString.data(using: String.Encoding.utf8)
let task = session.dataTask(with: request as URLRequest) {(data, response, error) in
//hide loading
LoadingOverlay.shared.hideOverlayView()
//no response
guard let data = data, let _:URLResponse = response, error == nil else {
print("response error")
return
}
//response, parse and send to build
let json = String(data: data, encoding: String.Encoding.utf8);
if let data = json?.data(using: String.Encoding.utf8){
let json = try! JSON(data: data)
for item in json["rows"].arrayValue {
//push data to arrays
self.booknames.append(item["name"].stringValue)
self.bookcolor.append(item["color"].stringValue)
self.bookdescription.append(item["description"].stringValue)
self.bookid.append(item["id"].int!)
//reload uicollection
DispatchQueue.main.sync(execute: {
self. bookscollection.reloadData()
})
}
}
}
task.resume()
}
//------------ collection -------------//
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
print("collection view code called")
return self.booknames.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = bookscollection.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath as IndexPath) as! BooksCell
cell.myLabel.text = self.booknames[indexPath.item]
cell.backgroundColor = UIColor.cyan // make cell more visible in our example project
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
print("You selected cell #\(indexPath.item)!")
}
//------------ end ---------------//
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
Thank you for any help!
Same problem i faced..your url having many images means reload t
dispatch_async(dispatch_get_main_queue(), {
self.collectionView.reloadData()
})
Related
I am trying to make api call and print the result at UICollectionView cellForItemAt function but UICollectionView is lagging while fetching data from api. I tried collection view prefetching api but I couldn't implement it. How can I solve lagging scroll problem? Here is the problem:
Here is my api call class:
class ApiManager {
static var url: String?
static var header: [String : String]?
static var httpMethod: String?
static let session = URLSession.shared
static func getTask(itemName: String, completion:#escaping ([String]) -> Void) -> URLSessionDataTask {
let url = URL(string: ApiManager.url!)
var request = URLRequest(url: url!, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0)
request.allHTTPHeaderFields = ApiManager.header!
request.httpMethod = ApiManager.httpMethod
let session = ApiManager.session
let dataTask = session.dataTask(with: request) { data, response, error in
if error == nil && data != nil {
if (error != nil) {
print(error!)
} else {
do {
if let dictionary = try JSONSerialization.jsonObject(with: data!, options: []) as? [[String:Any]] {
var itemArray = [String]()
for index in 0...dictionary.count - 1 {
if let items = dictionary[index][itemName] as? String {
itemArray.append(items)
}
}
completion(itemArray)
}
} catch {
}
}
}
}
return dataTask
}
}
Here is my ViewController code:
class ViewController: UIViewController {
#IBOutlet weak var collectionView: UICollectionView!
var quoteArray = [String]()
var categories = ["love", "motivational", "mothersday", "all"]
override func viewDidLoad() {
super.viewDidLoad()
collectionView.delegate = self
collectionView.dataSource = self
for category in categories {
ApiManager.url = "https://famous-quotes4.p.rapidapi.com/random?category=\(category)&count=1000"
}
ApiManager.header = ["x-rapidapi-host": "famous-quotes4.p.rapidapi.com", "x-rapidapi-key": "SECRET_API_KEY" ]
ApiManager.httpMethod = "GET"
ApiManager.getTask(itemName: "text") { items in
DispatchQueue.main.async {
self.quoteArray.append(contentsOf: items)
self.collectionView.reloadData()
}
}.resume()
}
}
Here is my cellForItemAt func code:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as? Cell else { fatalError("cell dequeue error") }
cell.label.text = self.quoteArray[indexPath.row]
return cell
}
I'm quite new to Swift 4, came from Android Studio into Xcode.
I'm having an issue with Collection Views, the thing is if I fill a struct decodable with pre-determined data the cell it's clickable and passes to the didSelectItemat function. But when I'm getting the said data from a JSON from the server it doesn't.
My question is if there anything that it's passing over my head, or if I'm missing something.
Here's the code that I'm using:
struct SelectJogos: Decodable{
let success: Bool
let jogos: [Jogos]
}
struct Jogos: Decodable{
let NumJogo: Int
let EquipaA: String
let EquipaB: String
let Fase: String
let Serie: String
let CodComp: String
let Data: String
let Hora: String
let Local: String
}
class SubmeteController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource {
#IBOutlet weak var collectionView: UICollectionView!
var jogo = [Jogos]()
override func viewDidLoad() {
super.viewDidLoad()
collectionView.dataSource = self
//User em sessão
let user = UserDefaults.standard.string(forKey: "username")
print(user!)
//MandaPost para o server
let url = URL(string: "http://yourexemple.com/something")
var request = URLRequest(url: url!)
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
request.httpMethod = "POST"
let postString = "clube=\(user!)&opera=select"
request.httpBody = postString.data(using: .windowsCP1252)
//Receve a info do sv
let task = URLSession.shared.dataTask(with: request){data, request, error in
guard let data = data, error == nil else{
print("error=\(error!)")
return
}
do{
let dataServer = try JSONDecoder().decode(SelectJogos.self, from: data)
if dataServer.success != false{
self.jogo = dataServer.jogos
print(dataServer.success)
}else{
print("nogames")
}
DispatchQueue.main.async {
print("Number of Data:\(self.jogo.count)")
self.collectionView.reloadData()
}
}catch let jsonERR{
print("Error DO: ", jsonERR)
}
}
task.resume()
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
print("clicked")
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return jogo.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "customCell", for: indexPath) as! SubmeteListaViewCell
cell.NumJogo.text = String(jogo[indexPath.row].NumJogo)
print(jogo[indexPath.row].CodComp)
print(indexPath)
return cell
}
}
You have to set the delegate in viewDidLoad
`
collectionView.delegate = self
I want to pass the data from my JSON Url to my collection view cell, so after parsing my json I have got array of URL links, the question is how to send it to cell imageView?
here is my code
import UIKit
class ItemsListViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource{
var myItems = [Item]()
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return myItems.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CollectionViewCell", for: indexPath) as! CollectionViewCell
cell.itemPriseLabel.text = myItems[indexPath.row].currentPrice
let imageUrl = myItems[indexPath.row].productImageUrl
print(imageUrl)
cell.itemImage.image? = imageUrl[indexPath.row]
return cell
}
var category1: Categories?
#IBOutlet weak var colectionViewVariable: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
downloadJSON3 {
self.colectionViewVariable.reloadData()
}
colectionViewVariable?.dataSource = self
colectionViewVariable?.delegate = self
// Do any additional setup after loading the view.
}
}
func getDataFromUrl(url: URL, completion: #escaping (Data?, URLResponse?, Error?) -> ()) {
URLSession.shared.dataTask(with: url) { data, response, error in
completion(data, response, error)
}.resume()
}
func downloadImage(url: URL) {
print("Download Started")
getDataFromUrl(url: url) { data, response, error in
guard let data = data, error == nil else { return }
print(response?.suggestedFilename ?? url.lastPathComponent)
print("Download Finished")
DispatchQueue.main.async() {
self.imageView.image = UIImage(data: data)
}
}
}
Duplicate: Answer found HERE I've stacked it into one concise answer but you should definitely read the answer as it is in the link as it is more complete and better altogether.
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CollectionViewCell", for: indexPath) as! CollectionViewCell
cell.itemPriseLabel.text = myItems[indexPath.row].currentPrice
if let imageUrl = myItems[indexPath.row].productImageUrl.first {
URLSession.shared.dataTask(with: imageUrl, completionHandler: { (data, response, error) in
guard let data = data, response != nil, error == nil else {return}
DispatchQueue.main.async(execute: { () -> Void in
let image = UIImage(data: data)
if let cell = collectionView.cellForItem(at: indexPath) as? CollectionViewCell {
cell.itemImage.image = image
}
})
}).resume()
}
return cell
}
I am new to Swift, and am trying to create a table that reads JSON data from my website. I followed some tutorials, and was able to get my code to work with a table controller. Now I'm trying to use a view controller with a table view inside, so I can have more customization. My problem is, I can't get the data to actually show up when I try to use my new code.
This is what I have in my viewController.swift:
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var tableView: UITableView!
var TableData:Array< String > = Array < String >()
override func viewDidLoad() {
super.viewDidLoad()
get_data_from_url("http://www.stevenbunting.org/Alliris/service.php")
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return TableData.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = TableData[indexPath.row]
return cell
}
func get_data_from_url(_ link:String)
{
let url:URL = URL(string: link)!
let session = URLSession.shared
let request = NSMutableURLRequest(url: url)
request.httpMethod = "GET"
request.cachePolicy = NSURLRequest.CachePolicy.reloadIgnoringCacheData
let task = session.dataTask(with: request as URLRequest, completionHandler: {
(
data, response, error) in
guard let _:Data = data, let _:URLResponse = response , error == nil else {
return
}
self.extract_json(data!)
})
task.resume()
}
func extract_json(_ data: Data)
{
let json: Any?
do
{
json = try JSONSerialization.jsonObject(with: data, options: [])
}
catch
{
return
}
guard let data_list = json as? NSArray else
{
return
}
if let countries_list = json as? NSArray
{
for i in 0 ..< data_list.count
{
if let country_obj = countries_list[i] as? NSDictionary
{
if let country_name = country_obj["user"] as? String
{
if let country_code = country_obj["friendlist"] as? String
{
TableData.append(country_name + " [" + country_code + "]")
}
}
}
}
}
DispatchQueue.main.async(execute: {self.do_table_refresh()
})
}
func do_table_refresh()
{
self.tableView.reloadData()
}
}
Probably you didn't set the tableView's dataSource. To do this, implement the UITableViewDataSource-protocol in the ViewController-class and set the tableView's dataSource-property to self in the viewDidLoad(), for example:
class ViewController: UIViewController, UITableViewDataSource {
// ...
override func viewDidLoad() {
super.viewDidLoad()
tableView.dataSource = self
// ...
}
//...
}
Oh, and don't forget about the Apple Transport Security-settings, otherwise you won't see anything as iOS doesn't allow HTTP anymore, you have use HTTPS. The right way to handle this is to get an SSL-Certificate for your domain.
The quick'n'dirty and absolutely not recommended way is to disable ATS or to set an exception for certain, trustworthy domains.
I am reading products data from API and print down the ids to make sure data has been fetched successfully. Then, I put products titles into collection view label.
The strange thing here is that the list of ids are printed very fast. Then the app wait for few seconds till the collectionView is populated.
I couldn't understand why the delay is occurring as I have only 10 products, which should not take any time for loading and I already made a loop over them and printed ids successfully in no time!
The following code shows exactly what I have done yet. I hope someone can help me figure out where is the bottle-nick:
import UIKit
class TestViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate {
#IBOutlet weak var collectionView: UICollectionView!
var jsonData : [[String: Any]] = [[:]]
override func viewDidLoad() {
super.viewDidLoad()
var request = URLRequest(url: URL(string: shopUrl + "/admin/products.json")!)
request.httpMethod = "GET"
URLSession.shared.dataTask(with:request, completionHandler: {(data, response, error) in
if error != nil {
print(error)
} else {
do {
guard let json = try? JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? [String: Any] else { return }
guard let root = json?["products"] as? [[String: Any]] else { return }
self.jsonData = root
self.collectionView.reloadData()
print("This will be printed very fast!")
for product in root {
guard let id = product["id"] as? Int else { return }
print(id)
}
}
}
}).resume()
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return self.jsonData.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let data = self.jsonData[indexPath.row]
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "TestCollectionViewCell", for: indexPath) as! TestCollectionViewCell
if let title = data["title"] as? String {
cell.titleLabel.text = title
}
return cell
}
}
Try call your reloadData in main thread:
DispatchQueue.main.async {
self.collectionView.reloadData()
}
The problem is that URLSession callback handler is still in background thread so it wont update your UI fast, so you have to switch back to main thread before update any UI after call network request with URLSession