Uitableview infinite scroll hang lag - swift

view the video of problem here
Im trying to make my infinite scroll work smoothly, before i had it working in a way where when the user reachs a certain item count in the table, it would load the others but while doing the load a Footer Activity indicator would appear, then dissapear and add the next batch of rows... which i kinda found annoying later on.. so i wanned to opmitized it. i want the tableview to initially load 100 items, then when the user is just around row 10 it would start to load the next 100 items and rows... the thing is.. everytime it makes the call.. the uitableview kinda hangs...
I did eliminate some trivial code here like the segmented control and letter buttons.. etc,
//
// VCrestlist.swift
// AsyncUITableview
import UIKit
import Foundation
import Alamofire
import SwiftyJSON
import Parse
class VCrestlist: UITableViewController{
let PageSize = 80
var items:[RestsModel] = []
var isLoading = false
var letter = "ALL"
var online = false
var loaded = false
var bigviewforact:UIView = UIView(frame: UIScreen.main.bounds)
#IBOutlet var MyFooterView : UIView!
override func viewDidLoad() {
super.viewDidLoad()
self.MyFooterView.isHidden = true
createButtons()
// Load custom Xib RestCell
let nib = UINib(nibName: "RestCell", bundle: nil)
// register cell identifier with custom Xib RestCell
self.tableView.register(nib, forCellReuseIdentifier: "Cell")
tableView.dataSource = self
loadSegment(0, size: self.PageSize,argletter:"ALL",online:self.online)
self.ButtonsScrollView.isScrollEnabled = true
}
class DataManager {
func requestData(_ offset:Int, size:Int,letter:String,online:Bool, listener:#escaping ([RestsModel]) -> ()) {
DispatchQueue.global(qos: .background).async {
var letra = ""
print("el offest es \(offset) y el size es \(size)")
if (letter != "") {
letra = letter
} else {
letra = "ALL"
}
let cart = Cart.sharedInstance
Alamofire.request("https://www.getmedata.com/rests.php", parameters: ["offset": "\(offset)","size":"\(size)","letra":"\(letra)","online":"\(online)"]).authenticate(usingCredential: cart.credential).responseJSON() {
response in
if(response.result.value != nil) {
let jsonObj = JSON(response.result.value!);
let rests = jsonObj as JSON
// print(jsonObj);
//generate items
if let restsarray = rests["rests"].arrayValue as [JSON]? {
var arr = [RestsModel]()
//3
for restDict in restsarray {
let restName: String? = restDict["nombre"].stringValue
let restLOGO: String? = restDict["logo"].stringValue
let detURL: String? = restDict["url"].stringValue
let detProvincia: String? = restDict["provincia"].stringValue
let detHorario: String? = restDict["horario"].stringValue
let detDireccion: String? = restDict["direccion"].stringValue
let detTipoComida: String? = restDict["tipo_comida"].stringValue
let detTelefono: String? = restDict["telefono"].stringValue
let detidRest: String? = restDict["id"].stringValue
let detalleDelivery: String? = restDict["alldelivery"].stringValue
let detalleOrderOnline: String? = restDict["orderonline"].stringValue
let detalleReservaOnline: String? = restDict["reservasonline"].stringValue
let dethasSchedule: Bool? = restDict["hasSchedule"].boolValue
let detopenNow: Bool? = restDict["opennow"].boolValue
let detscheduleToday: String? = restDict["scheduleToday"].stringValue
let detscheduleTodayLiteral: String? = restDict["scheduleTodayLiteral"].stringValue
let emp_instagram: String? = restDict["emp_instagram"].stringValue
let emp_e_orderonline: String? = restDict["emp_e_orderonline"].stringValue
let emp_e_reservasonline: String? = restDict["emp_e_reservasonline"].stringValue
let emp_e_kids: String? = restDict["emp_e_kids"].stringValue
let emp_e_pickup: String? = restDict["emp_e_pickup"].stringValue
let emp_e_delivery: String? = restDict["emp_e_delivery"].stringValue
let emp_e_wifi: String? = restDict["emp_e_wifi"].stringValue
let emp_e_valet: String? = restDict["emp_e_valet"].stringValue
let emp_e_exterior: String? = restDict["emp_e_exterior"].stringValue
let emp_e_happyhour: String? = restDict["emp_e_happyhour"].stringValue
let emp_e_desayuno: String? = restDict["emp_e_desayuno"].stringValue
let emp_e_fumar: String? = restDict["emp_e_fumar"].stringValue
let emp_e_vinos: String? = restDict["emp_e_vinos"].stringValue
let emp_e_bar: String? = restDict["emp_e_bar"].stringValue
let emp_e_games: String? = restDict["emp_e_games"].stringValue
let rest = RestsModel(name: restName!,image:restLOGO!,detailURL:detURL!,provincia:detProvincia!,tipocomida:detTipoComida!,idRest:detidRest!,hasSchedule: dethasSchedule!,scheduleToday: detscheduleToday!,scheduleTodayLiteral: detscheduleTodayLiteral!,openNow: detopenNow!,allDelivery: detalleDelivery!,orderonline: detalleOrderOnline!,reservaonline: detalleReservaOnline!, instagram: emp_instagram!, emp_e_kids: emp_e_kids!, emp_e_pickup: emp_e_pickup!, emp_e_delivery: emp_e_delivery!, emp_e_wifi: emp_e_wifi!, emp_e_valet: emp_e_valet!, emp_e_exterior: emp_e_exterior!, emp_e_happyhour: emp_e_happyhour!, emp_e_desayuno: emp_e_desayuno!, emp_e_fumar: emp_e_fumar!, emp_e_vinos: emp_e_vinos!, emp_e_bar: emp_e_bar!, emp_e_games: emp_e_games!, horario: detHorario!, direccion: detDireccion! ,telefono: detTelefono!)
arr.append(rest)
}
print(arr)
//call listener in main thread
DispatchQueue.main.async {
listener(arr)
}
}
}
}
}
///
}
}
func loadSegment(_ offset:Int, size:Int,argletter:String,online:Bool) {
if (self.loaded == false) {
let act:UIActivityIndicatorView = UIActivityIndicatorView(activityIndicatorStyle: UIActivityIndicatorViewStyle.gray)
bigviewforact.addSubview(act)
act.center = bigviewforact.center
self.view.addSubview(bigviewforact)
self.view.bringSubview(toFront: bigviewforact)
act.hidesWhenStopped = true
act.startAnimating()
//self.bigviewforact.removeFromSuperview()
self.loaded = true
}
//if (!self.isLoading) {
// self.isLoading = true
//self.MyFooterView.isHidden = (offset==0) ? true : false
let manager = DataManager()
manager.requestData(offset, size: size,letter:argletter,online:online,
listener: {(items:[RestsModel]) -> () in
/*
Add Rows at indexpath
*/
for item in items {
self.tableView.beginUpdates()
let row = self.items.count
let indexPath = IndexPath(row:row,section:0)
self.items += [item]
self.tableView?.insertRows(at: [indexPath], with: UITableViewRowAnimation.fade)
self.tableView.endUpdates()
}
self.bigviewforact.removeFromSuperview()
self.isLoading = false
self.MyFooterView.isHidden = true
}
)
// }
}
override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
if indexPath.row == items.count-70 {
loadSegment(items.count, size: PageSize,argletter:self.letter,online:self.online)
}
}
// MARK: Table view data source
override func numberOfSections(in tableView: UITableView?) -> Int {
return 1
}
override func tableView(_ tableView: UITableView?, numberOfRowsInSection section: Int) -> Int {
return items.count
}
override func tableView(_ tableView: UITableView?, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 85;
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell : RestCell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! RestCell
// Pass data to Cell :) clean the mess at the View Controller ;)
cell.restData = items[indexPath.row]
cell.tag = indexPath.row
return cell
}
}

Don't insert each item into UITableView. Insert all of them together.
var indexPathes: [IndexPath] = []
var newItems: [RestsModel] = []
for item in items {
let row = self.items.count
let indexPath = IndexPath(row:row,section:0)
newItems.append(item)
indexPathes.append(indexPath)
}
self.items.append(contentsOf: newItems)
self.tableView.beginUpdates()
self.tableView?.insertRows(at: indexPathes, with: UITableViewRowAnimation.fade)
self.tableView.endUpdates()

Related

Index out of range during tableView.reloadData

I am having performance issues and index out of range errors during some refreshes. Not all, but I am struggling to troubleshoot it.
Basically, the app will collect data from an RSS feed and display it in a tableview. This is my first time using the libraries that I have, and my first time trying to properly make tableview app.
I believe the refresh error has to do with where I am refresh() as well as where I am calling projects.removeAll(). I have tried moving them to other places and I still get the refresh issue.
I am not sure why all of a sudden I have scrolling lag. If anyone has time to quickly review my code and give me suggestions, as well as try and help me fix the refresh section of the code (to fix the array out of bounds error) that would be amazing.
Thanks.
Here is my complete code.
//
// FirstViewController.swift
//
//
import UIKit
import SWXMLHash
import Alamofire
import SafariServices
class FirstViewController: UITableViewController, SFSafariViewControllerDelegate {
var projects = [[String]]()
var xmlToParse = String()
var postTitle = String()
var postLink = String()
var postAuthor = String()
var postThumbnail = String()
var postComments = String()
var postPublishDate = String()
var postVotes = String()
var postVotesNeg = String()
var dateFormatter = DateFormatter()
override func viewDidLoad() {
super.viewDidLoad()
httpRequest("https://www.test.com/rss") { response in
}
// set up the refresh control
self.refreshControl?.attributedTitle = NSAttributedString(string: "Pull to refresh")
self.refreshControl?.addTarget(self, action: #selector(refresh), for: UIControlEvents.valueChanged)
self.tableView?.addSubview(refreshControl!)
self.dateFormatter.dateStyle = DateFormatter.Style.short
self.dateFormatter.timeStyle = DateFormatter.Style.long
}
func setup() {
tableView.reloadData()
print("Refreshed")
}
#objc func refresh(sender:AnyObject) {
let now = NSDate()
let updateString = "Last Updated at " + self.dateFormatter.string(from: now as Date)
self.refreshControl?.attributedTitle = NSAttributedString(string: updateString)
if (self.refreshControl?.isRefreshing)!
{
self.refreshControl?.endRefreshing()
projects.removeAll()
}
httpRequest("https://www.test.com/rss") { response in
}
}
func enumerate(indexer: XMLIndexer, level: Int) {
for child in indexer.children {
let name = child.element!.name
print("\(level) \(name)")
enumerate(indexer: child, level: level + 1)
}
}
func httpRequest(_ section: String, completion: #escaping (String) -> Void) {
Alamofire.request(section, method: .get).responseString { response in
self.xmlToParse = response.result.value!
let xml = SWXMLHash.parse(self.xmlToParse)
for elem in xml["rss"]["channel"]["item"].all {
self.postTitle = elem["title"].element!.text
self.postLink = elem["link"].element!.text
self.postAuthor = elem["dc:creator"].element!.text
self.postThumbnail = (elem["media:thumbnail"].element?.attribute(by: "url")?.text)!
self.postComments = elem["comments"].element!.text
self.postPublishDate = elem["pubDate"].element!.text
self.postVotes = (elem["test:meta"].element?.attribute(by: "votes-pos")?.text)!
self.postVotesNeg = (elem["test:meta"].element?.attribute(by: "votes-neg")?.text)!
self.projects.append([self.postTitle, self.postLink, self.postAuthor, self.postThumbnail, self.postPublishDate, self.postComments, self.postVotes, self.postVotesNeg])
}
completion(response.result.value!)
self.setup()
}
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if let url = URL(string: self.projects[indexPath.row][1]) {
let vc = SFSafariViewController(url: url)
vc.delegate = self
present(vc, animated: true)
}
}
func safariViewControllerDidFinish(_ controller: SFSafariViewController) {
dismiss(animated: true)
}
func makeAttributedString(title: String, subtitle: String) -> NSAttributedString {
let titleAttributes = [NSAttributedStringKey.font: UIFont.preferredFont(forTextStyle: .headline), NSAttributedStringKey.foregroundColor: UIColor.black]
let subtitleAttributes = [NSAttributedStringKey.font: UIFont.preferredFont(forTextStyle: .subheadline), NSAttributedStringKey.foregroundColor: UIColor.gray]
let titleString = NSMutableAttributedString(string: "\(title)\n", attributes: titleAttributes)
let subtitleString = NSAttributedString(string: subtitle, attributes: subtitleAttributes)
titleString.append(subtitleString)
return titleString
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return projects.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
let project = projects[indexPath.row]
cell.textLabel?.attributedText = makeAttributedString(title: project[0], subtitle: "Author: " + project[2] + "\nPost Date: " + project[4] + "\nUpvotes: " + project[6] + "\nDownvotes: " + project[7] )
let imageURL = URL(string: project[3])
let data = try? Data(contentsOf: imageURL!)
if data != nil {
let image = UIImage(data: data!)
cell.imageView?.image = image
}
return cell
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}

Swift 3 table search

I have populated a table using json data from a remote server.
I am now trying to add a search bar which will filter the results.
The issue I am facing is that I am storing the json data in several arrays.
name, company, job title etc
This means that when the user searches only the name array is filtered and displayed in the table correctly, the other information is out of sync as it remains unfiltered.
Am I approaching this in the correct way?
class attendees: UIViewController, UITableViewDelegate, UITableViewDataSource, UISearchBarDelegate {
var tableData = ""
var value:String!
var searchString = ""
var firstname: [String] = []
var lastname: [String] = []
var fullname: [String] = []
var company: [String] = []
var jobtitle: [String] = []
var image: [String] = []
var filteredAppleProducts = [String]()
var resultSearchController = UISearchController()
#IBOutlet weak var tableView: UITableView!
#IBOutlet weak var searchBar: UISearchBar!
override func viewDidLoad() {
print(value)
searchBar.delegate = self
self.tableView.reloadData()
let nib = UINib(nibName: "vwTblCell2", bundle: nil)
tableView.register(nib, forCellReuseIdentifier: "cell2")
}
override func viewDidAppear(_ animated: Bool) {
getTableData()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if filteredAppleProducts != []{
return self.filteredAppleProducts.count
}
else
{
if searchString != "[]" {
return self.firstname.count
}else {
return 0
}
}
}
// 3
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell2: TblCell2 = self.tableView.dequeueReusableCell(withIdentifier: "cell2") as! TblCell2
print(filteredAppleProducts)
if filteredAppleProducts != []{
cell2.nameLabel.text = self.filteredAppleProducts[indexPath.row]
return cell2
}
else
{
if searchString != "[]"{
cell2.nameLabel.text = "\(self.firstname[indexPath.row]) \(self.lastname[indexPath.row])"
cell2.companyLabel.text = self.company[indexPath.row]
cell2.jobTitleLabel.text = self.jobtitle[indexPath.row]
let url = URL(string: "https://www.asmserver.co.uk/wellpleased/backend/profileimages/\(self.image[indexPath.row])")
let data = try? Data(contentsOf: url!) //make sure your image in this url does exist, otherwise unwrap in a if let check / try-catch
cell2.userImage.image = UIImage(data: data!)
}
return cell2
}
}
// 4
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
}
// 5
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 90
}
func updateSearchResults(){
self.filteredAppleProducts.removeAll(keepingCapacity: false)
let searchPredicate = NSPredicate(format: "SELF CONTAINS[c] %#", searchString)
let array = (self.fullname as NSArray).filtered(using: searchPredicate)
self.filteredAppleProducts = array as! [String]
self.tableView.reloadData()
print(filteredAppleProducts)
}
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
print("searchText \(searchText)")
print(filteredAppleProducts)
searchString = searchText
updateSearchResults()
}
func getTableData(){
self.firstname.removeAll()
self.lastname.removeAll()
self.fullname.removeAll()
self.company.removeAll()
self.jobtitle.removeAll()
self.image.removeAll()
let defaults = UserDefaults()
let userid = defaults.string(forKey: "id")
let url = NSURL(string: "https://www.asmserver.co.uk/wellpleased/backend/searchattendees.php?userid=\(userid!)&eventid=\(value!)")
print(url)
let task = URLSession.shared.dataTask(with: url as! URL) { (data, response, error) -> Void in
if let urlContent = data {
do {
if let jsonResult = try JSONSerialization.jsonObject(with: urlContent, options: []) as? [[String:AnyObject]] {
var i = 0
while i < jsonResult.count {
self.firstname.append(jsonResult[i]["firstname"]! as! String)
self.lastname.append(jsonResult[i]["lastname"]! as! String)
let fname = jsonResult[i]["firstname"]! as! String
let lname = jsonResult[i]["lastname"]! as! String
let fullname1 = "\(fname) \(lname)"
self.fullname.append(fullname1)
self.company.append(jsonResult[i]["company"]! as! String)
self.jobtitle.append(jsonResult[i]["jobtitle"]! as! String)
self.image.append(jsonResult[i]["image"]! as! String)
i = i + 1
}
}
} catch {
print("JSON serialization failed")
}
} else {
print("ERROR FOUND HERE")
}
DispatchQueue.main.async(execute: { () -> Void in
self.tableView.reloadData()
})
self.tableView.isUserInteractionEnabled = true
}
task.resume()
}
}
Use a struct or class for the data. This way it'll be easier to keep track of the data, and in any case it looks like you don't have any good reason to keep track of seven different arrays.
As an example:
struct Data {
var firstName: String
var lastName: String
var fullName: String
var company: String
var jobTitle: String
var image: String
}
And populate just the one array:
var dataSource: [Data] = []
Access with property name, instead of arrayName[index]:
let name = dataSource[index].firstName

Swift 3 Custom TableViewCell Error

I am having trouble communicating with the "TableView". How can I solve the problem?
Error Image
Code Image
var arrayOfData = [cellData]()
var fullname1 : String!
var code1 : String!
var name1 : String!
var buy1 : Double!
var sell1 : Double!
var change_rate1 : Double!
var update_date1 : Double!
var arrayFullName : [String] = ["asdas","asds","asds","sadas","asdasd","asdas"]
#IBOutlet weak var tblDoviz: UITableView!
#IBOutlet weak var openBarButton: UIBarButtonItem!
override func viewDidLoad() {
super.viewDidLoad()
arrayOfData = [cellData(fullName: "Amerikan Doları", name: "", code: "USD", updateDate: 2, changeRate: 2, buy: 3.44, sell: 3.47)]
openBarButton.target = self.revealViewController()
openBarButton.action = Selector("revealToggle:")
tblDoviz.delegate = self
tblDoviz.dataSource = self
self.view.addGestureRecognizer(self.revealViewController().panGestureRecognizer())
/* Doviz Sitesinden Bilgiler Çekiliyor */
let url = NSURL(string: "http://www.doviz.com/api/v1/currencies/all/latest")
let task = URLSession.shared.dataTask(with: url as! URL){(data,response,error) ->Void in
if error != nil
{
}
else
{
if let urlContent = data
{
do
{
let jsonResult = try JSONSerialization.jsonObject(with: urlContent, options: JSONSerialization.ReadingOptions.mutableContainers) as AnyObject
if let currencyRate = jsonResult as? NSArray
{
for i in 0..<currencyRate.count
{
if let name = (currencyRate[i] as? NSDictionary)?["name"] as? String
{
self.name1 = name
}
if let fullname = (currencyRate[i] as? NSDictionary)?["full_name"] as? String
{
self.fullname1 = fullname
}
if let change_rate = (currencyRate[i] as? NSDictionary)?["change_rate"] as? Double
{
self.change_rate1 = change_rate
}
if let code = (currencyRate[i] as? NSDictionary)?["code"] as? String
{
self.code1 = code
}
if let update_date = (currencyRate[i] as? NSDictionary)?["update_date"] as? Double
{
self.update_date1 = update_date
}
if let buying = (currencyRate[i] as? NSDictionary)?["buying"] as? Double
{
self.buy1 = buying
}
if let selling = (currencyRate[i] as? NSDictionary)?["selling"] as? Double
{
self.sell1 = selling
}
//Array'a Yükleme Yapılıyor
//cellData.init(fullName: self.fullname1, name: self.name1, code: self.code1, updateDate: self.update_date1, changeRate: self.change_rate1, buy: self.buy1, sell: self.sell1)
//self.arrayOfData = [cellData(fullName: self.fullname1, name: self.name1, code : self.code1, updateDate: self.update_date1, changeRate: self.change_rate1, buy: self.buy1, sell: self.sell1)]
//print(self.arrayOfData)
//print(cellData())
}
print(self.arrayFullName.count)
}
}
catch
{
}
}
}
}
task.resume()
//--------------------------------------------------
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 2
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cellDoviz",for:indexPath) as! DovizCell1
Error-------------------------------------
cell.lblText.text = arrayFullName[indexPath.row]
Error -----------------------------------
return cell as DovizCell1
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 94
}
}
Try adding this to your code
let cell:DovizCell? = tableView.dequeueReusableCell(withIdentifier: "cellDoviz",for:indexPath) as! DovizCell
if cell == nil {
cell = NSBundle.mainBundle().loadNibNamed("DovizCell").first as! DovizCell
}
Your DovizCell might have been nil which is why the code crashed

How to access a property of a tableview cell from viewcontroller?

Please see screenshot. There is a repliesTableView, replyTextField and replyButtonin ViewController. repliesTableView cell is called ReplyCell. In ReplyCell there is a commentTableView to list all comments for that reply and a textfField, a commentButton to add new comments.
I have problem when add new replies and new comments. I guess I need to make comments array in ReplyCell empty when I click the Reply button. How can I make this happen? I have no idea how to access comments arrayfrom the root ViewController.
Exact problems: fter clicking commentButton, all comments in every cell doubled. After clicking replyButton, comments went to wrong cell.
Code:
import UIKit
import Firebase
class TopicForumVC: UIViewController, UITableViewDelegate, UITableViewDataSource, UITextFieldDelegate {
#IBOutlet weak var topicNameLabel: UILabel!
#IBOutlet weak var replyNumberLabel: UILabel!
#IBOutlet weak var repliesTableView: UITableView!
#IBOutlet weak var replyTextField: UITextField!
var topicName:String?
var firstKey:String?
var secondKey:String?
var replies = [String]()
var replyButtonTapped = false
override func viewDidLoad() {
super.viewDidLoad()
repliesTableView.delegate = self
repliesTableView.dataSource = self
replyTextField.delegate = self
}
override func viewDidAppear(_ animated: Bool) {
topicNameLabel.text = self.topicName
loadReplies()
}
func loadReplies() {
self.replies = []
DataService.ds.Categories_Base.child(self.firstKey!).child("Topics").observe(.value, with:{(snapshot) in
if let snapshots = snapshot.children.allObjects as? [FIRDataSnapshot] {
for snap in snapshots {
if let topicDict = snap.value as? Dictionary<String,AnyObject> {
if let topic = topicDict["text"] as? String {
if topic == self.topicName {
self.secondKey = snap.key
UserDefaults.standard.setValue(snap.key, forKey: Key_SecondKey)
if let replyDict = topicDict["replies"] as? Dictionary<String,AnyObject> {
for eachDict in replyDict {
if let textDict = eachDict.value as? Dictionary<String,AnyObject> {
if let reply = textDict["text"] as? String {
self.replies.append(reply)
self.replyNumberLabel.text = String(self.replies.count)
}
}
}
}
}
}
}
}
self.repliesTableView.reloadData()
}
})
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return replies.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if let cell = tableView.dequeueReusableCell(withIdentifier: "ReplyCell") as? ReplyCell {
let reply = replies[indexPath.row]
cell.configureReplyCell(reply: reply)
return cell
} else {
return UITableViewCell()
}
}
#IBAction func replyButtonTapped(_ sender: Any) {
replyButtonTapped = true
if let reply = replyTextField.text, reply != "" {
self.replies = []
DataService.ds.Categories_Base.child(self.firstKey!).child("Topics").child(self.secondKey!).child("replies").childByAutoId().child("text").setValue(reply)
self.repliesTableView.reloadData()
let i = replies.count
for n in 0..<i {
let indexPath = IndexPath(row: n, section: 1)
let cell = repliesTableView.cellForRow(at: indexPath) as! ReplyCell
cell.comments = []
cell.repliesToReplyTableView.reloadData()
}
self.replyTextField.text = ""
self.replyButtonTapped = false
}
}
}
import UIKit
import Firebase
class ReplyCell: UITableViewCell,UITableViewDataSource,UITableViewDelegate, UITextFieldDelegate {
#IBOutlet weak var replyTextView: UITextView!
#IBOutlet weak var repliesToReplyTableView: UITableView!
#IBOutlet weak var commentTextField: UITextField!
var reply:String?
var comments = [String]()
var replyKey:String?
override func awakeFromNib() {
super.awakeFromNib()
self.comments = []
repliesToReplyTableView.delegate = self
repliesToReplyTableView.dataSource = self
commentTextField.delegate = self
loadComments()
}
func configureReplyCell(reply:String) {
self.reply = reply
self.replyTextView.text = self.reply
}
func loadComments() {
self.comments = []
if let firstKey = UserDefaults.standard.value(forKey: Key_FirstKey) as? String, let secondKey = UserDefaults.standard.value(forKey: Key_SecondKey) as? String {
DataService.ds.Categories_Base.child(firstKey).child("Topics").child(secondKey).child("replies").observe(.value, with:{(snapshot) in
if let snapshots = snapshot.children.allObjects as? [FIRDataSnapshot] {
for snap in snapshots {
if let replyDict = snap.value as? Dictionary<String,AnyObject> {
if let reply = replyDict["text"] as? String {
if reply == self.reply {
self.replyKey = snap.key
DataService.ds.Categories_Base.child(firstKey).child("Topics").child(secondKey).child("replies").child(snap.key).child("comments").observe(.value, with: { (commentSnapshot) in
if let commentSnapshots = commentSnapshot.children.allObjects as? [FIRDataSnapshot] {
for commentSnap in commentSnapshots {
if let commentDict = commentSnap.value as? Dictionary<String,AnyObject> {
if let comment = commentDict["text"] as? String {
self.comments.append(comment)
}
}
}
}
self.repliesToReplyTableView.reloadData()
})
}
}
}
}
}
})
}
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return comments.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let commentCell = tableView.dequeueReusableCell(withIdentifier:"CommentCell")
commentCell?.textLabel?.text = comments[indexPath.row]
return commentCell!
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
#IBAction func commentBtnPressed(_ sender: Any) {
if let comment = commentTextField.text, comment != "" {
self.comments = []
if let firstKey = UserDefaults.standard.value(forKey: Key_FirstKey) as? String, let secondKey = UserDefaults.standard.value(forKey: Key_SecondKey) as? String {
DataService.ds.Categories_Base.child(firstKey).child("Topics").child(secondKey).child("replies").child(self.replyKey!).child("comments").childByAutoId().child("text").setValue(comment)
if let myViewController = parentViewController as? TopicForumVC {
// myViewController.repliesTableView.reloadData()
myViewController.replies = []
}
self.repliesToReplyTableView.reloadData()
self.commentTextField.text = ""
self.replyKey = ""
}
}
}
I don't really know the exact circumstances of what you're building but there are two ideas that may offer some guidance.
1) If your table is displaying content from a data source then you will likely have some kind of reference. E.g. when loading the cells (in this case CustomCell) you'll do something like get the index of the cell and get the same index from the data, and put that data in the cells content. If that's the case, all you have to do on the button click is use tableview.cellForRowAtIndexPath with your sender object, and then remove the array from the data source, e.g. tableDataSource[index] = nil and reload the tableView.
2) If you have a stored property on the CustomCell that you've add specifically for storing this array, then you'd cast the sender object to CustomCell and remove the property, as in Kim's answer.
Hope this helps, but without more information it's kind of hard to tell.
let cell = tableview.cellForRowAtIndexPath(...) as? CustomCell
if cell != nil {
let arr = cell.array
}
BTW: I would re-think storing your array in the cell..

Tutorial in retrieving, mutating and saving array from Parse.com in Swift with UITableView

import UIKit
class FeedTableViewController: UITableViewController {
var navBar:UINavigationBar=UINavigationBar()
let font = UIFont(name: "Baskerville", size: 15)
var feedData:NSMutableArray = NSMutableArray()
required init(coder aDecoder: NSCoder){
super.init(coder: aDecoder)
}
#IBAction func likeButton(sender: AnyObject) {
if var votes:Int? = quote!.objectForKey("votes") as? Int {
votes!++
}
}
#IBAction func loadData(sender: AnyObject?) {
feedData.removeAllObjects()
var findFeedData:PFQuery = PFQuery(className: "userQuotes")
findFeedData.findObjectsInBackgroundWithBlock{
(objects:[AnyObject]?, error:NSError?)->Void in
if error == nil{
if let objs = objects{
for object in objs{
let quote:PFObject = object as! PFObject
self.feedData.addObject(quote)
// let user:PFUser = (object as! NSArray).lastObject as! PFUser
}
//println(self.feedData)
let array:NSArray = self.feedData.reverseObjectEnumerator().allObjects
self.feedData = NSMutableArray(array: array)
NSOperationQueue.mainQueue().addOperationWithBlock({
self.tableView.reloadData()
})
}
}
}
}
override func viewDidAppear(animated: Bool) {
self.loadData( nil )
}
override func viewDidLoad() {
super.viewDidLoad()
self.title = "Quotezilla"
// 3
//self.navigationItem.setRightBarButtonItem(rightSearchBarButtonItem, animated: true)
// Uncomment the following line to preserve selection between presentations
// self.clearsSelectionOnViewWillAppear = false
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Potentially incomplete method implementation.
// Return the number of sections.
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete method implementation.
// Return the number of rows in the section.
return feedData.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell:QuoteTableViewCell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! QuoteTableViewCell
let quote:PFObject = self.feedData.objectAtIndex(indexPath.row) as! PFObject
cell.contentTextView!.font = font
cell.timeStampLabel!.font = font
cell.publisherLabel!.font = font
cell.contentTextView.alpha = 0
cell.timeStampLabel.alpha = 0
cell.publisherLabel.alpha = 0
cell.contentTextView.text = quote.objectForKey("content") as! String
//cell.publisherLabel.text = quote.objectForKey("publisher") as? String
/* func loadLikes(){
if var votes:Int? = quote.objectForKey("votes") as? Int {
votes!++
}
}*/
var dateFormatter:NSDateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "EEEE, MMM d, h:mm a"
cell.timeStampLabel.text = dateFormatter.stringFromDate(quote.createdAt!)
var votes:Int? = quote["votes"] as? Int
if votes == nil {
votes = 0
}
cell.likesLabel?.text = "\(votes!)"
var myObject = quote["publisher"] as? PFObject
myObject?.fetchIfNeeded()
if let foundUser = myObject as? PFUser{
cell.publisherLabel.text = foundUser.username
UIView.animateWithDuration(0.7, animations: {
cell.contentTextView.alpha = 1
cell.timeStampLabel.alpha = 1
cell.publisherLabel.alpha = 1
})
}
return cell
}
So what I am essentially attempting to do is create a likes or votes button. As you see in the code I have a likeButton action that is supposed to auto-increment the likes section in parse. I display the current likes that I have filled into the rows in Parse itself in the cellForRowAtIndexPath function. The problem is that I cannot call quote.objectForKey("votes"), because I initialize it later. I have been poring over this problem and cannot find a way to make the votes update in parse through the likeButton action.
You must live with life on the network. That means your table won't have certain data available when the App starts. Handle a missing object or missing key within a particular cell gracefully and just use some kind of placeholder value. When the parse callback executes, you are already correctly forcing a refresh.
OK So BIG EDIT
This class needed a lot of work. I'm not even going to spell out every change here, but it's basically a complete Parse.com tutorial at this point.
This code compiles cleanly but I can't be sure of everything in your context. In particular do you have a 'likesButton' on every table row as part of your custom table cell view? I'm assuming that.
class FeedTableViewController: UITableViewController {
var navBar = UINavigationBar()
let font = UIFont(name: "Baskerville", size: 15)
var feedData = [PFObject]()
static let cellID = "cell"
// NOTE! See how this tag is set below
#IBAction func likeButton(sender: UIButton) {
let quote = feedData[sender.tag]
if let votes = quote.objectForKey("votes") as? Int {
quote.setObject(votes + 1, forKey: "votes")
}
else {
// CHALLENGE FOR YOU: handle the case of no votes attribute
}
// UPDATE the local UI
tableView.reloadRowsAtIndexPaths([NSIndexPath(forRow: sender.tag, inSection: 0)],
withRowAnimation: .None)
// CHALLENGE FOR YOU: UPDATE Parse...start a new question if necessary
}
#IBAction func loadData(sender: AnyObject?) {
feedData.removeAll()
PFQuery(className: "userQuotes").findObjectsInBackgroundWithBlock {
[unowned self]
(objects: [AnyObject]?, error: NSError?) -> Void in
if let objs = objects {
for object in objs {
self.feedData.append(object as! PFObject)
}
self.feedData = self.feedData.reverse()
}
NSOperationQueue.mainQueue().addOperationWithBlock { self.tableView.reloadData() }
}
}
override func viewDidLoad() {
super.viewDidLoad()
self.loadData(nil)
self.title = "Quotezilla"
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return feedData.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier(FeedTableViewController.cellID, forIndexPath: indexPath) as! QuoteTableViewCell
cell.likesButton!.tag = indexPath.row // See how tag works with the above
cell.contentTextView!.font = font
cell.timeStampLabel!.font = font
cell.publisherLabel!.font = font
cell.contentTextView.alpha = 0.0
cell.timeStampLabel.alpha = 0.0
cell.publisherLabel.alpha = 0.0
let q = feedData[indexPath.row]
if let content = q.objectForKey("content") as? String {
cell.contentTextView.text = content
}
else {
cell.contentTextView.text = "Content not found!"
}
var dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "EEEE, MMM d, h:mm a"
cell.timeStampLabel.text = dateFormatter.stringFromDate(q.createdAt!)
let votes = (q.objectForKey("votes") as? Int) ?? 0
cell.likesLabel?.text = "\(votes)"
let myObject = q.objectForKey("publisher") as? PFObject
myObject?.fetchInBackgroundWithBlock {
[unowned self]
(object: PFObject?, error: NSError?) in
NSOperationQueue.mainQueue().addOperationWithBlock {
if let foundUser = object as? PFUser {
cell.publisherLabel.text = foundUser.username
UIView.animateWithDuration(0.7) {
cell.contentTextView.alpha = 1.0
cell.timeStampLabel.alpha = 1.0
cell.publisherLabel.alpha = 1.0
}
}
else {
cell.publisherLabel.text = "Publisher not found!"
}
}
}
return cell
}
}