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
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 have backend api it contains all values i can see those values in postman.. but while parsing i am unable to download all values from api.. some times i am getting all values.. some times i am not getting only some values.. if i close app and run again then i am getting all values.. again if i close and run or if i go to other viewcontroller and coming back to home then i am missing some values. if i print jsonObj i am not getting all values from api.. why is this happening?
here is my code:
import UIKit
import SDWebImage
struct JsonData {
var iconHome: String?
var typeName: String?
var id: String?
init(icon: String, tpe: String, id: String) {
self.iconHome = icon
self.typeName = tpe
self.id = id
}
}
class HomeViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource, UITextFieldDelegate {
#IBOutlet weak var collectionView: UICollectionView!
var itemsArray = [JsonData]()
override func viewDidLoad() {
super.viewDidLoad()
homeServiceCall()
//Do any additional setup after loading the view.
collectionView.delegate = self
collectionView.dataSource = self
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return itemsArray.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! HomeCollectionViewCell
let aData = itemsArray[indexPath.row]
cell.paymentLabel.text = aData.typeName
cell.paymentImage.sd_setImage(with: URL(string:aData.iconHome ?? ""), placeholderImage: UIImage(named: "varun finance5_icon"))
return cell
}
//MARK:- Service-call
func homeServiceCall(){
let urlStr = "https://dev.com/webservices//getfinancer"
let url = URL(string: urlStr)
URLSession.shared.dataTask(with: url!, completionHandler: {(data, response, error) in
guard let respData = data else {
return
}
guard error == nil else {
print("error")
return
}
do{
let jsonObj = try JSONSerialization.jsonObject(with: respData, options: .allowFragments) as! [String: Any]
print("the home json is \(jsonObj)")
let financerArray = jsonObj["financer"] as! [[String: Any]]
for financer in financerArray {
guard let id = financer["id"] as? String else { break }
guard let pic = financer["icon"] as? String else { break }
guard let typeName = financer["tpe"] as? String else { break } //changed this one to optional too. Avoid force-unwrapping. Keep everything safe
let jsonDataObj = JsonData(icon: pic, tpe: typeName, id: id)
self.itemsArray.append(jsonDataObj)
}
DispatchQueue.main.async {
self.collectionView.reloadData()
}
}
catch {
print("catch error")
}
}).resume()
}
}
Please help me in the above code.
Try network call with background thread.
DispatchQueue.global(qos: DispatchQoS.QoSClass.background).async {
self.homeServiceCall()
}
And the URL is legal with double / as below ?
let urlStr = "https://dev.com/webservices//getfinancer"
And check the all of your backend data's is compatible with your JSONSerialization type of JsonData struct.
I hope it helps.
I want to know how to get each name from the parsed json response in swift. I want to assign the name value of the reponse to cell.label.text based on my code cell.label.text = item.name. Label will automate depending on how many item.name , what I want is that it will automate depending on how many name there is from json response
if indexPath.row == 0 {
// get a reference to our storyboard cell
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "AddNewCell", for: indexPath as IndexPath) as! AddNewCollectionViewCell
cell.backgroundColor = unselectedCellColor // make cell more visible in our example project
return cell
} else {
let index = IndexPath(row: (indexPath.row - 1), section: 0)
let item = fetchedResultsController.object(at: index)
// get a reference to our storyboard cell
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath as IndexPath) as! GroceryItemCollectionViewCell
cell.buttonDelegate = self
cell.deleteButton.tag = indexPath.row - 1
cell.label.text = item.name
if item.isSelected {
cell.backgroundColor = selectedCellColor
} else {
cell.backgroundColor = unselectedCellColor
}
return cell
}
Http request code:
responseString = Optional("[{\"id\":51,\"name\":\"jelord\",\"desc\":\"ako si jelord\",\"reward\":\"1.00\",\"sched\":\"2018-04-06T11:37:09+08:00\",\"parent\":null,\"child\":null,\"occurrence\":{\"name\":\"once\"},\"status\":\"created\"},{\"id\":53,\"name\":\"uuuuuu\",\"desc\":\"uuuuu\",\"reward\":\"8.00\",\"sched\":\"2018-03-06T10:49:54+08:00\",\"parent\":null,\"child\":null,\"occurrence\":{\"name\":\"once\"},\"status\":\"created\"},{\"id\":54,\"name\":\"iiiii\",\"desc\":\"oiii\",\"reward\":\"67.00\",\"sched\":\"2018-02-06T10:51:34+08:00\",\"parent\":null,\"child\":null,\"occurrence\":{\"name\":\"once\"},\"status\":\"created\"},{\"id\":55,\"name\":\"uuuu\",\"desc\":\"uuuu\",\"reward\":\"8.00\",\"sched\":\"2018-03-06T10:52:55+08:00\",\"parent\":null,\"child\":null,\"occurrence\":{\"name\":\"once\"},\"status\":\"created\"},{\"id\":57,\"name\":\"uuuuuuuu\",\"desc\":\"uuuuuu\",\"reward\":\"8888.00\",\"sched\":\"2018-04-06T11:54:16.431000+08:00\",\"parent\":null,\"child\":null,\"occurrence\":{\"name\":\"once\"},\"status\":\"created\"},{\"id\":61,\"name\":\"hhu\",\"desc\":\"yhh\",\"reward\":\"67.00\",\"sched\":\"2018-02-06T13:45:09+08:00\",\"parent\":null,\"child\":null,\"occurrence\":{\"name\":\"once\"},\"status\":\"created\"},{\"id\":62,\"name\":\"huhu\",\"desc\":\"huu\",\"reward\":\"8.00\",\"sched\":\"2018-04-06T14:46:36.620000+08:00\",\"parent\":null,\"child\":null,\"occurrence\":{\"name\":\"once\"},\"status\":\"created\"}]")
code for getting the request.
var request = URLRequest(url: URL(string: "http://test.test:8000/api/v1/test/")!)
request.httpMethod = "GET"
let task = URLSession.shared.dataTask(with: request) { data, response, error in
guard let data = data, error == nil else { // check for fundamental networking error
print("error=\(String(describing: error))")
return
}
if let httpStatus = response as? HTTPURLResponse, httpStatus.statusCode != 200 { // check for http errors
print("statusCode should be 200, but is \(httpStatus.statusCode)")
print("response = \(String(describing: response))")
}
let responseString = String(data: data, encoding: .utf8)
print("responseString = \(String(describing: responseString))")
}
task.resume()
import UIKit
import Alamofire
class MenuCollectionViewController: UIViewController,
UICollectionViewDelegate, UICollectionViewDataSource {
var titleArray = [String]()
#IBOutlet var collectionView: UICollectionView!
#IBAction func signOutButtonIsPressed(_ sender: Any) {
let appDelegate : AppDelegate = UIApplication.shared.delegate as! AppDelegate
appDelegate.showLoginScreen()
}
#IBOutlet var signoutButton: UIButton!
var items = [Item]()
override func viewDidLoad() {
super.viewDidLoad()
self.signoutButton.layer.cornerRadius = 3.0
demoApi()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.navigationController?.navigationBar.isHidden = true
self.navigationItem.hidesBackButton = true
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return titleArray.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! CollectionCell
cell.nameLabel.text = titleArray[indexPath.row]
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
// handle tap events
print("You selected cell #\(indexPath.item)!")
}
func demoApi() {
Alamofire.request("https://jsonplaceholder.typicode.com/posts", method: .get, parameters: nil, encoding: JSONEncoding.default, headers: nil).responseJSON { (response:DataResponse<Any>) in
switch(response.result) {
case .success(_):
guard let json = response.result.value as! [[String:Any]]? else{ return}
print("Response \(json)")
for item in json {
if let title = item["title"] as? String {
self.titleArray.append(title)
}
DispatchQueue.main.async {
self.collectionView.reloadData()
}
}
break
case .failure(_):
print("Error")
break
}
}
}
}
class CollectionCell: UICollectionViewCell {
#IBOutlet weak var imgPhoto: UIImageView!
#IBOutlet weak var nameLabel: UILabel!
}
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 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()
})