Collectionview in TableviewCell, data repeat - swift

I've a collectionview inside my resizable tablviewCells. Tableview has one cell in each 'n' number of sections. Datasource and delegate of collectionview are set to the tableviewCell. There is an API called on tablview's cellForRowAt, and the result is rendered on the collectionview for each cell. After the result is fetched, a delegate tells the tableview that collectionview is loaded and it should reload that cell without calling the API this time. But the problem is that my collectionview data is repeated after every 2 tableviewCells.
I know prepareForReuse should be override to get rid of cell reuse problems. I've implemented prepareForReuse in my collectionviewCells and set my label.text and imageView.image to nil. However i'm not sure what to add to prepareForReuse for my tableviewCell.
// TableView class
override func viewDidLoad() {
super.viewDidLoad()
storiesSections = [....]
tableView.register(UINib(nibName: "RWFeedTableViewCell", bundle: nil), forCellReuseIdentifier: "reuseIdentifier")
tableView.estimatedRowHeight = 1
tableView.rowHeight = UITableView.automaticDimension
}
override func numberOfSections(in tableView: UITableView) -> Int {
return storiesSections.count
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier", for: indexPath) as! RWFeedTableViewCell
if cell == nil {
cell = UITableViewCell(style: UITableViewCell.CellStyle.default, reuseIdentifier: "reuseIdentifier") as! RWFeedTableViewCell
}
cell.delegate = self
cell.fetchData(feedSection: storiesSections[indexPath.section], indexPath: indexPath)
return cell
}
// delegate for tableview reload
func collectionViewDidEnd(updatedFeedSection: FeedSection, indexPath: IndexPath) {
storiesSections[indexPath.section] = updatedFeedSection
tableView.beginUpdates()
tableView.endUpdates()
}
// TableViewCell class
override func awakeFromNib() {
super.awakeFromNib()
initializeCode()
}
func initializeCode() {
// Set layout
self.collectionView.collectionViewLayout = RWWaterfallLayout2()
self.collectionView.register(UINib(nibName: "\(ImageThenTitleViewCell.self)", bundle: nil), forCellWithReuseIdentifier: kImageThenTitleCellID)
self.collectionView.register(UINib(nibName: "\(LeftImageCell.self)", bundle: nil), forCellWithReuseIdentifier: kLeftImageCellID)
self.collectionView.contentInset = UIEdgeInsets(top: 8, left: 8, bottom: 8, right: 8)
self.collectionView.isScrollEnabled = false
self.collectionView.dataSource = self
self.collectionView.delegate = self
}
func fetchData(feedSection: FeedSection, indexPath: IndexPath) {
if feedSection.isLoadComplete {
return
}
if let catID = feedSection.categoryID {
let urlString = URL(string: <urlString>)
let urlRequest = URLRequest(url: urlString!)
let config = URLSessionConfiguration.default
let session = URLSession(configuration: config)
let task = session.dataTask(with: urlRequest) { (data, response, error) in
if error == nil {
} else {
print(error?.localizedDescription as Any)
}
guard let responseData = data else {
print("Error: did not receive data")
return
}
do {
guard let todo = try JSONSerialization.jsonObject(with: responseData, options: [])as? [String: Any] else {
print("error trying to convert data to JSON")
return
}
print("success convert data to JSON")
DispatchQueue.main.async {
var updatedFeedSection = feedSection
updatedFeedSection.storiesArray? = (todo["data"]! as! Array)
updatedFeedSection.isLoadComplete = true
self.feedSection = updatedFeedSection
self.collectionView.reloadData()
self.collectionView.performBatchUpdates({
}, completion: { (complete) in
self.collectionViewHeightConstraint.constant = self.collectionView.collectionViewLayout.collectionViewContentSize.height + self.collectionView.contentInset.top + self.collectionView.contentInset.bottom
self.delegate?.collectionViewDidEnd(updatedFeedSection: updatedFeedSection, indexPath: indexPath)
})
}
} catch {
print("error trying to convert data to JSON")
return
}
}
task.resume()
}
}
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
if self.feedSection == nil {
return 0
} else {
return (self.feedSection?.storiesArray?.count)!
}
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let indexForTen = indexPath.item%10
let story = self.feedSection?.storiesArray?[indexPath.item]
if indexForTen == 0 {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: kImageThenTitleCellID, for: indexPath) as! ImageThenTitleViewCell
cell.setupData(story: story!)
return cell
}
else {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: kLeftImageCellID, for: indexPath) as! LeftImageCell
cell.setupData(story: story!)
return cell
}
}
override func prepareForReuse() {
super.prepareForReuse()
}
// Collectionview Cell
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
func setupData(story: Dictionary<String, Any>){
self.storyImage.image = nil // reset the image
let thumbImage = story["image"] as! Dictionary<String, String>
self.storyTitle.text = story["t"] as? String
self.storyImage.downloaded(from: (thumbImage["m"])!)
self.layer.borderColor = UIColor.lightGray.cgColor
self.layer.borderWidth = 1
self.layer.cornerRadius = 8
}
override func prepareForReuse() {
super.prepareForReuse()
storyImage.image = nil
storyTitle.text = nil
}
// FeedSection struct
struct FeedSection {
var categoryID: String?
var storiesArray : [Dictionary<String, Any>]?
var isLoadComplete: Bool
init(categoryID: String) {
self.categoryID = categoryID
self.storiesArray = []
self.isLoadComplete = false
}
}
Currently the 3rd tableviewCell repeats the data of 1st tablviewCell. How to avoid repeating cell data?

Only problem was the feedSection object in TableViewCell. It should be initialized at the time fetchData() is called. And just reload the collectionView if isLoadComplete is true.
Also since isLoadComplete is set on completion handler of URLSession, I set it to true the time API is called. So the same api will not be called while waiting for response. Maybe an enum could be set for api call and api response events on FeedSection. But for now this works.
func fetchData(feedSection: FeedSection, indexPath: IndexPath) {
self.feedSection = feedSection
if self.feedSection.isLoadComplete {
self.collectionView.reloadData()
return
}
if let catID = feedSection.categoryID {
let urlString = URL(string: <urlString>)
let urlRequest = URLRequest(url: urlString!)
let config = URLSessionConfiguration.default
let session = URLSession(configuration: config)
self.feedSection.isLoadComplete = true
self.delegate?.collectionViewDidEnd(updatedFeedSection: self.feedSection, indexPath: indexPath)
let task = session.dataTask(with: urlRequest) { (data, response, error) in
if error == nil {
} else {
print(error?.localizedDescription as Any)
}
guard let responseData = data else {
print("Error: did not receive data")
return
}
do {
guard let todo = try JSONSerialization.jsonObject(with: responseData, options: [])as? [String: Any] else {
print("error trying to convert data to JSON")
return
}
print("success convert data to JSON")
DispatchQueue.main.async {
self.feedSection.storiesArray? = (todo["data"]! as! Array)
self.feedSection.isLoadComplete = true
self.collectionView.reloadData()
self.collectionView.performBatchUpdates({
}, completion: { (complete) in
self.collectionViewHeightConstraint.constant = self.collectionView.collectionViewLayout.collectionViewContentSize.height + self.collectionView.contentInset.top + self.collectionView.contentInset.bottom
self.delegate?.collectionViewDidEnd(updatedFeedSection: self.feedSection, indexPath: indexPath)
})
}
} catch {
print("error trying to convert data to JSON")
return
}
}
task.resume()
}
}

Set UITableView.reloadData() after get the data.
And check if you set CollectionView.reloadData(), if yes then remove reloadData() of UICollectionView. Only set UITableView.reloadData()

Related

Index out of range custom cell TableViewController

I'm trying to populate 3 custom cells into a TableViewController.
but I always get index out of range error. I`m not sure whats wrong with my code. anyone can help me, I'm newbie in swift.
but when i use 0 for numberOfRowsInSection return, the output is the first cell.
here's my code :
class testResize: UITableViewController {
#objc var comments = [AnyObject]()
#objc var images = [UIImage]()
var getImg = [String]()
override func viewDidLoad() {
super.viewDidLoad()
loadPosts()
tableView.estimatedRowHeight = 100
tableView.rowHeight = UITableViewAutomaticDimension
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 3
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row == 0 {
let getCom = comments[indexPath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: testResizeHeadCell.self), for: indexPath) as! testResizeHeadCell
let user = getCom["nickname"] as! String
let ava = getCom["ava"] as! String
if ava != "" {
let resource = ImageResource(downloadURL: URL(string: ava)!, cacheKey: ava)
cell.avaImg.kf.setImage(with: resource)
}
cell.username.text = user
return cell
}else if indexPath.row == 1 {
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: testResizeCell.self), for: indexPath) as! testResizeCell
cell.setCustomImage(image: images[indexPath.row])
return cell
}else {
let getCom = comments[indexPath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: testRezieTextCell.self), for: indexPath) as! testRezieTextCell
let text = getCom["text"] as! String
cell.explaination.text = text
return cell
}
}
here is my load function :
#objc func loadPosts() {
let uuid = "959D1073"
let url = URL(string: "some/url.php")!
self.tableView.reloadData()
var request = URLRequest(url: url)
request.httpMethod = "POST"
let body = "uuid=\(uuid)"
//print(body)
request.httpBody = body.data(using: String.Encoding.utf8)
URLSession.shared.dataTask(with: request) { data, response, error in
DispatchQueue.main.async(execute: {
if error == nil {
do{
let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? NSDictionary
self.comments.removeAll(keepingCapacity: false)
self.images.removeAll(keepingCapacity: false)
self.tableView.reloadData()
guard let parseJSON = json else {
print("Error While Parsing")
return
}
guard let posts = parseJSON["posts"] as? [AnyObject] else {
print("Error while parseJSONing")
return
}
self.comments = posts.reversed()
print(self.comments)
for i in 0 ..< self.comments.count {
let path = self.comments[i]["path"] as? String
self.getImg = [path!]
if !path!.isEmpty {
let url = NSURL(string: path!)!
let imageData = try? Data(contentsOf: url as URL)
let image = UIImage(data: imageData! as Data)!
self.images.append(image)
} else {
let image = UIImage()
self.images.append(image)
}
}
self.tableView.reloadData()
//print(posts)
} catch {
print(error)
}
}else{
print(error!)
}
})
}.resume()
}
i think you have a single comment and 3 cell type and when you use indexPath.row happen some thing like this :
for example :
comments = {[{nickname : "mahdi" , ava : "url"} ]}
if indexPath.row == 0 {
let getCom = comments[0]
let user = getCom["nickname"] as! String
let ava = getCom["ava"] as! String
}else if indexPath.row == 1 {
images[1]
}else {
let getCom = comments[2]
let text = getCom["text"] as! String
}
but you have just one comment and when you call comments[1] or commens [2] , you get index out of range error
please try this code :
class testResize:UITableViewController {
#objc var comments = [AnyObject]()
#objc var images = [UIImage]()
var getImg = [String]()
override func viewDidLoad() {
super.viewDidLoad()
loadPosts()
tableView.estimatedRowHeight = 100
tableView.rowHeight = UITableViewAutomaticDimension
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return (self.comments.count == 0 ? 0 : self.comments.count + 2)
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row == 0 {
let getCom = comments[indexPath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: testResizeHeadCell.self), for: indexPath) as! testResizeHeadCell
let user = getCom["nickname"] as! String
let ava = getCom["ava"] as! String
if ava != "" {
let resource = ImageResource(downloadURL: URL(string: ava)!, cacheKey: ava)
cell.avaImg.kf.setImage(with: resource)
}
cell.username.text = user
return cell
}else if indexPath.row == 1 {
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: testResizeCell.self), for: indexPath) as! testResizeCell
cell.setCustomImage(image: images[indexPath.row - 1])
return cell
}else {
let getCom = comments[indexPath.row - 2]
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: testRezieTextCell.self), for: indexPath) as! testRezieTextCell
let text = getCom["text"] as! String
cell.explaination.text = text
return cell
}
}
and change your numberOfRowInSection :
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return (self.comments.count == 0 ? 0 : self.comments.count + 2)
}
I am assuming that you load your posts asynchronously.
But you do not check if there are actually enough elements in the array. You should check if there are actually enough elements in the array before you access it with a fixed index.
Additionally, you should change your numberOfRows method to the following:
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return self.comments.count
}
After you have loaded your posts, you can then call
self.tableView.reloadData()

I want to get firestore data in dequeueReusableCell

I rewrote all the text and now I got the code I wanted to realize.
It can not be displayed on the tableCell, and the layout also collapses. I am sorry that the code and the body I wrote are not explained enough.
guard let userID = Auth.auth (). currentUser? .uid I want to always acquire userID with else {return}.
// guard let docSnapshot = querySnapshot, document.exists else {return}
Since an error occurs, it is commented out.
Within viewidLoad of UIViewController
var profDict: [ProfDic] = [] is in the UIViewController.
profUIView is being added to UIViewController.
func getFirebaseData() {
db = Firestore.firestore()
guard let userID = Auth.auth().currentUser?.uid else {return}
let ref = db.collection("users").document(userID)
ref.getDocument{ (document, error) in
if let document = document {
// guard let docSnapshot = querySnapshot, document.exists else {return}
if let prof = ProfDic(dictionary: document.data()!) {
self.profDict.append(prof)
print("Document data \(document.data())")
}
}else{
print("Document does not exist")
}
self.profUIView.tableView1.reloadData()
}
}
tableView1 has been added to ProfUIView.
class ProfUIView: UIView, UITableViewDelegate, UITableViewDataSource {
//omission...
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = .blue
addSubview(tableView1)
tableView1.anchor(top: //omission...
sections = [
Section(type: .prof_Sec, items: [.prof]),
Section(type: .link_Sec, items: [.link]),
Section(type: .hoge_Sec, items: [.hoge0])
]
tableView1.register(TableCell0.self, forCellReuseIdentifier: TableCellId0)
tableView1.register(TableCell3.self, forCellReuseIdentifier: TableCellId3)
tableView1.register(TableCell5.self, forCellReuseIdentifier: TableCellId5)
tableView1.delegate = self
tableView1.dataSource = self
}
var tableView1:UITableView = {
let table = UITableView()
table.backgroundColor = .gray
return table
}()
//omission
func numberOfSections(in tableView: UITableView) -> Int {
return sections.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return (baseVC?.profDict.count)!//sections[section].items.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
switch sections[indexPath.section].items[indexPath.row] {
case .prof:
let cell0 = tableView.dequeueReusableCell(withIdentifier: TableCellId0, for: indexPath) as? TableCell0
cell0?.nameLabel.text = baseVC?.profDict[indexPath.row].userName
return cell0!
}
//omission...
}
}
Additional notes
import Foundation
import FirebaseFirestore
struct ProfDic {
var userName :String
var dictionary:[String:Any] {
return
["userName" : userName
]
}
}
extension ProfDic {
init?(dictionary:[String:Any]) {
guard let userName = dictionary["userName"] as? String
else {return nil}
self.init(userName: userName as String)
}
}
enter image description here
First create an empty array of ProfDic elements:
var profDict: [ProfDic] = []
Then create a function to load your Firebase Data:
func getFirebaseData() {
db = Firestore.firestore()
let userRef = db.collection("users").getDocuments() {
[weak self] (querySnapshot, error) in
for document in querySnapshot!.documents {
guard let docSnapshot = docSnapshot, docSnapshot.exists else {return}
if let prof = ProfDic(dictionary: docSnapshot.data()!) {
profDict.append(prof)
}
}
tableView.reloadData()
}
}
Call this function in viewDidLoad or viewDidAppear.
Then in tableView cellForRowAt you access your data like this:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
switch sections[indexPath.section].items[indexPath.row] {
case .prof:
let cell = tableView.dequeueReusableCell(withIdentifier: TableCellId, for: indexPath) as? TableCell
cell?.nameLabel.text = profDict[indexPath.row].userName
return cell!
}
}
EDIT:
Also in numberOfRowsInSection:
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return profDict.count
}

Xcode tableview not displaying firebase objects

I am currently trying to display firebase objects in my tableview. However, I am only getting empty prototype cells. Thanks if you can take a glance at it!
import UIKit
import Firebase
class UsersTableViewController: UITableViewController {
var user = [User]()
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func getusers() {
let userID = Auth.auth().currentUser?.uid
let rootRef = Database.database().reference()
let query = rootRef.child("users").queryOrdered(byChild: "fullname")
query.observe(.value) { (snapshot) in
for child in snapshot.children.allObjects as! [DataSnapshot] {
if let value = child.value as? NSDictionary {
let userToShow = User()
let fullname = value["fullname"] as? String ?? "Name not found"
let uid = value["uid"] as? String ?? "uid not found"
userToShow.fullname = fullname
userToShow.userID = uid
self.user.append(userToShow)
DispatchQueue.main.async { self.tableView.reloadData() }
}
}
}
}
override func numberOfSections(in tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return user.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "userCell", for: indexPath) as! TableViewCell
cell.nameLabel.text = self.user[indexPath.row].fullname
cell.userID = self.user[indexPath.row].userID
cell.userImage.downloadImage(from: self.user[indexPath.row].imagePath!)
return cell
}
}
extension UIImageView {
#objc func downloadImage(from imgURL: String!) {
let url = URLRequest(url: URL(string: imgURL)!)
let task = URLSession.shared.dataTask(with: url) {
(data, response, error) in
if error != nil {
print(error!)
return
}
DispatchQueue.main.async {
self.image = UIImage(data: data!)
}
}
task.resume()
}}
Thanks again for anyone willing to help! I am currently using Swift 4 with Google's Firebase API.
p.s. I know that I have connected delegate and dataSource. I am also sure that I have properly titled my Identifier. Thanks!

Swift Table index out of Range

I am new to swift and have set up a table which fills using data from an sql database.
The table loads fine but occasionally it gives the error:
"Fatal Error: Index out of range".
It doesn't happen all the time just every now and again.
Also I have migrated from parse to using sql and http requests. Have I taken the correct approach to this when populating the data into the table?
Any help much appreciated!
#IBOutlet var tableView: UITableView!
var tableData = [String]()
var tableImages = [String]()
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidAppear(animated: Bool) {
if Reachability.isConnectedToNetwork() == true {
self.tableView.hidden = true
self.tableData.removeAll(keepCapacity: true)
self.tableImages.removeAll(keepCapacity: true)
var nib = UINib(nibName: "vwTblCell3", bundle: nil)
tableView.registerNib(nib, forCellReuseIdentifier: "cell3")
let request = NSURLRequest(URL: NSURL(string: "********.php")!)
NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue(), completionHandler:{
(response: NSURLResponse?, data: NSData?, error: NSError?)-> Void in
let str2 = String(data: data!, encoding: NSUTF8StringEncoding)
let str3 = Int(str2!)!
let url = NSURL(string: "********")!
let task = NSURLSession.sharedSession().dataTaskWithURL(url) { (data, response, error) -> Void in
if let urlContent = data {
do {
let jsonResult = try NSJSONSerialization.JSONObjectWithData(urlContent, options: NSJSONReadingOptions.MutableContainers)
print(str3)
var i = 0
while i < str3 {
print(jsonResult[i]["title"]! as! String)
print(jsonResult[i]["image"]! as! String)
self.tableData.append(jsonResult[i]["title"]! as! String)
self.tableImages.append(jsonResult[i]["image"]! as! String)
i = i + 1
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.tableView.reloadData()
})
}
} catch {
print("JSON serialization failed")
}
}
}
task.resume()
});
print(tableData)
self.tableView.hidden = false
}
}
// 2
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.tableData.count
}
// 3
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell: TblCell3 = self.tableView.dequeueReusableCellWithIdentifier("cell3") as! TblCell3
cell.lblAffiliate.text = tableData[indexPath.row]
let url3 = NSURL(string: "https://www.********.co.uk/\(tableImages[(indexPath as NSIndexPath).row]).png")
cell.affiliateImage.sd_setImageWithURL(url3)
return cell
}
// 4
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
print("Row \(indexPath.row) selected")
}
// 5
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return 400
}
}
I hope this helps. I changed a couple small things around for better code (half could be considered bias). I think the issue is mostly that you were reloading the tableView in the loop. Everything else was just a slightly better way to handle this case. I put everything in viewDidLoad, and made the tableView load empty input prequel to receiving data. I think this is more standard for handling this scenario. If you need any other help let me know.
class ViewController: UIViewController {
#IBOutlet var tableView: UITableView!
var tableData: [String] = []
var tableImages: [String] = []
override func viewDidLoad() {
super.viewDidLoad()
if Reachability.isConnectedToNetwork() == true {
var nib = UINib(nibName: "vwTblCell3", bundle: nil)
tableView.registerNib(nib, forCellReuseIdentifier: "cell3")
let request = NSURLRequest(URL: NSURL(string: "********.php")!)
NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue(), completionHandler:{
(response: NSURLResponse?, data: NSData?, error: NSError?)-> Void in
let str2 = String(data: data!, encoding: NSUTF8StringEncoding)
let str3 = Int(str2!)!
let url = NSURL(string: "********")!
let task = NSURLSession.sharedSession().dataTaskWithURL(url) { (data, response, error) -> Void in
if let urlContent = data {
do {
let jsonResult = try NSJSONSerialization.JSONObjectWithData(urlContent, options: NSJSONReadingOptions.MutableContainers)
self.tableData = []
self.tableImages = []
for i in 0..<str3 {
self.tableData.append(jsonResult[i]["title"]! as! String)
self.tableImages.append(jsonResult[i]["image"]! as! String)
}
dispatch_async(dispatch_get_main_queue()) {
self.tableView.reloadData()
}
} catch {
print("JSON serialization failed")
}
}
}
task.resume()
});
}
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.tableData.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell: TblCell3 = self.tableView.dequeueReusableCellWithIdentifier("cell3") as! TblCell3
cell.lblAffiliate.text = tableData[indexPath.row]
let url3 = NSURL(string: "https://www.********.co.uk/\(tableImages[(indexPath as NSIndexPath).row]).png")
cell.affiliateImage.sd_setImageWithURL(url3)
return cell
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
print("Row \(indexPath.row) selected")
}
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return 400
}
}
The problem is that your call to reloadData() is inside the while loop in which you are building tableData and tableImages. Move that after the while loop, by which point both of those arrays will be fully populated.

Ambiguous reference to member tableview number rows in section

I'm loading some data from a web server and I'm getting ambiguous reference to member tableview numberOfRowsInSection? Below is my swift file. What am i missing here?
TableViewController.swift
import UIKit
class TableViewController: UITableViewController {
var viewModel:ViewModel!
override func viewDidLoad() {
super.viewDidLoad()
self.viewModel = ViewModel()
self.tableView.delegate = viewModel
self.tableView.dataSource = viewModel
self.tableView.registerNib(UINib(nibName: "TableViewCell", bundle: NSBundle.mainBundle()), forCellReuseIdentifier: tableViewCellIdentifier)
self.tableView.rowHeight = UITableViewAutomaticDimension
self.tableView.estimatedRowHeight = 50;
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
ViewModel.swift
import UIKit
class ViewModel: NSObject,UITableViewDelegate, UITableViewDataSource, UICollectionViewDelegate, UICollectionViewDataSource {
var dataArrayPosts: NSArray!
var posts:[Post] = []
override init() {
super.init()
let myURL = NSURL(string: "mydomainURLhere");
let requestPosts = NSMutableURLRequest(URL: myURL!);
requestPosts.HTTPMethod = "POST";
let postStringVars = ""
requestPosts.HTTPBody = postStringVars.dataUsingEncoding(NSUTF8StringEncoding);
let taskPosts = NSURLSession.sharedSession().dataTaskWithRequest(requestPosts){ data, response, error in
if error != nil{
print("error\(error)");
return;
}
do {
if let json = try NSJSONSerialization.JSONObjectWithData(data!, options: []) as? NSDictionary {
//print(json)
if let category = json["Posts"] as? NSMutableArray{
//print(category)
_ = category
self.dataArrayPosts = json["Posts"] as! NSMutableArray;
dispatch_async(dispatch_get_main_queue()) {
self.posts.removeAll()
for item in self.dataArrayPosts {
let post = Post(postDesc: (item["postDesc"] as? String)!,imageName: (item["imageName"] as? String)!,bName: (item["bName"] as? String)!,postDate: (item["postDate"] as? String)!,postShowsUntil:(item["postShowsUntil"] as? String)!)
self.posts.append(post)
// Ambiguous on reloadData???????
self.tableView.reloadData()
}
}
}else{
dispatch_async(dispatch_get_main_queue()) {
print("Nothing Was Found")
}
}
}
} catch let error as NSError {
print(error.localizedDescription)
}
}
taskPosts.resume()
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return posts.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier(tableViewCellIdentifier, forIndexPath: indexPath) as! TableViewCell
let post = posts[indexPath.row]
cell.quoteTextLabel.text = post.postDesc
cell.nameLabel.text = post.bName
if let imageName = post.imageName where !imageName.isEmpty{
cell.photoView?.image = UIImage(named: imageName)
cell.photoWidthConstraint.constant = kDefaultPhotoWidth
cell.photoRightMarginConstraint.constant = kDefaultPhotoRightMargin
}
else {
cell.photoView?.image = nil
cell.photoWidthConstraint.constant = 0
cell.photoRightMarginConstraint.constant = 0
}
cell.contentView.setNeedsLayout()
cell.contentView.layoutIfNeeded()
return cell
}
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return posts.count
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(collectionViewCellIdentifier, forIndexPath: indexPath) as! CollectionViewCell
return cell
}
}
None of the things that ViewModel inherits from expose a tableView property, your ViewModel needs a reference to your UITableViewController. As the ViewController owns the ViewModel it should be a weak reference, something like the following
class ViewModel: NSObject,UITableViewDelegate, UITableViewDataSource, UICollectionViewDelegate, UICollectionViewDataSource {
weak let tableViewController:UITableViewController
var dataArrayPosts: NSArray!
var posts:[Post] = []
override init(controller:UITableViewController) {
self.tableViewController = controller
super.init()
// ...
self.tableViewController.tableView.reloadData()
}
class TableViewController: UITableViewController {
var viewModel:ViewModel!
override func viewDidLoad() {
super.viewDidLoad()
self.viewModel = ViewModel(controller:self)
// ...
}