I am stuck on this minor issue, I have a tableviewcontroller which is also searchresultcontroller. I am getting correct data against each api call but tableview is not reloading. I have no clue why its not working. Any help or lead will be very much appreciated.
class MasterViewController: UITableViewController,UISearchResultsUpdating {
var request:DataRequest?
var peopleArr:[Peoples] = []
// MARK: - View Setup
override func viewDidLoad() {
super.viewDidLoad()
self.title = "Search"
definesPresentationContext = true
}
override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 50.0
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if indexPath.section == 1 {
// if searchController.searchBar.selectedScopeButtonIndex == 0 {
let profileVc = self.storyboard?.instantiateViewController(withIdentifier: "profileVc") as! ProfileController
profileVc.profileData = (peopleArr[indexPath.row].user_id, peopleArr[indexPath.row].user_id)
self.navigationController?.pushViewController(profileVc, animated: true)
}
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return peopleArr.count
}
override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
return tableView.dequeueReusableCell(withIdentifier: "headerPeopleSec")
}
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return "People"
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "FollowingsCell", for: indexPath) as! FollowingsCell
cell.textLabel?.text = "\(indexPath.row)"
let people: Peoples
people = peopleArr[indexPath.row]
if people.following == "1" {
cell.followBtn.isHidden = true
}
else{
cell.followBtn.isHidden = false
}
cell.profile_thumb!.showImageWithURL(urlString: people.photo_url)
cell.addAction = { cell in
self.addFriendAction(indexPath: indexPath , user:people)
}
cell.profile_thumb.motionIdentifier = people.user_id
cell.username.text = people.user_name
return cell
}
func getPeopleList(searchString:String?) {
if let req = self.request {
req.cancel()
}
let peopleBag = [
"auth_token": (MemberProfile.loggedUser?._auth_token())!,
"per_page": 30,
"page": 1,
"search_key": searchString ?? ""
] as [String : Any]
NVActivityIndicatorPresenter.sharedInstance.startAnimating(activityData)
self.request = HelperClass().doGetRequestCustom(url: BASE_URL + SEARCH_PEOPLE, param:peopleBag, header: [:], completion: {(response,responseObject, error) in
if let resMsg = (responseObject?.message.resp_status) {
NVActivityIndicatorPresenter.sharedInstance.stopAnimating()
// if let hasNext = responseObject?.message.paging_data.next_page_exist as? Bool {
// self.hasNextPage = hasNext
// }
let dictionary:[String: AnyObject]? = responseObject?.message.data as? [String:AnyObject] //["member_followings"]
if let dict:Array = dictionary?["member_profiles"] as? Array<[String:AnyObject]>{
for dic in dict {
let friend = Peoples()
friend.photo_url = (dic["photo"] as? String) ?? ""
friend.user_name = ((dic["user"]?["username"])! as String)
friend.user_id = (dic["id"])! as! String
friend.following = (dic["is_following"])! as! String
self.peopleArr.append(friend)
}
self.tableView.reloadData()
}
else{
}
}
else{
NVActivityIndicatorPresenter.sharedInstance.stopAnimating()
}
NVActivityIndicatorPresenter.sharedInstance.stopAnimating()
})
}
func addFriendAction(indexPath:IndexPath , user:Peoples) {
let followBag = [
"auth_token": (MemberProfile.loggedUser?.auth_token)!,
"following_profile_id": user.user_id
] as [String : Any]
NVActivityIndicatorPresenter.sharedInstance.startAnimating(activityData)
HelperClass().doPostRequest(url: BASE_URL+FOLLOW_MEMBER , param: followBag, completion: { (dataResponse,response,error) in
if (response != nil) && (response?.message.resp_status)!
{
NVActivityIndicatorPresenter.sharedInstance.stopAnimating()
let cell = self.tableView.cellForRow(at: indexPath) as! FollowingsCell
cell.followBtn.isHidden = true
user.following = "1"
}
else
{
if (response != nil){
NVActivityIndicatorPresenter.sharedInstance.stopAnimating()
HelperClass.showAlertViewWithTitle(title: "Error", Text: (response?.message.message)!, controllerToShowOn: self)
}
else{
NVActivityIndicatorPresenter.sharedInstance.stopAnimating()
HelperClass.showAlertViewWithTitle(title: "Error", Text: "Something went wrong. Please check your internet connection & try again later.", controllerToShowOn: self)
}
return
}
})
}
func updateSearchResults(for searchController: UISearchController) {
if !(searchController.searchBar.text! == "") {
self.peopleArr.removeAll()
self.tableView.reloadData()
let searchBar = searchController.searchBar
self.getPeopleList(searchString: searchBar.text!)
}
}
}
You need to make your reload call on the main thread:
...
for dic in dict {
let friend = Peoples()
friend.photo_url = (dic["photo"] as? String) ?? ""
friend.user_name = ((dic["user"]?["username"])! as String)
friend.user_id = (dic["id"])! as! String
friend.following = (dic["is_following"])! as! String
self.peopleArr.append(friend)
}
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.tableView.reloadData()
})
...
All UI modification always has to take place on the main thread. Most of the time you're in a completion handler you'll have to dispatch to main to modify the UI.
Seems something wrong in func updateSearchResults(for searchController: UISearchController).
Can you try moving self.tableView.reloadData() at the end of this function ?
It seems when reloadData is called, the array as cleared, and not yet populated with new values.
Related
well I'm trying to add date to my header sections every time the user is done with his task. the problem is im kinda new with headers section and for some reason every time I add more than two tasks into the "Done" section, it seems like it duplicates itself.
have looked around stackOverFlow and couldn't find what I needed, and I hope u guys can help me up :)
Here's a pic of the app with the problem: https://imgur.com/wjVy9Uy
Here's my code:
import Foundation
import CoreData
import UIKit
import SwipeCellKit
protocol TaskDelegate {
func updateTaskName(name:String)
}
class TasksManViewController: UITableViewController, SwipeTableViewCellDelegate {
#IBOutlet weak var Sege: UISegmentedControl!
var tasksArray = [Task](){
didSet {
// because we perform this operation on the main thread, it is safe
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
}
let isSwipeRightEnabled = true
var delegate: TaskDelegate?
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
override func viewDidLoad() {
}
override func viewWillAppear(_ animated: Bool) {
loadTasks()
}
// MARK: - DataSource + Delegate Methods:
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return tasksArray.count
}
override func numberOfSections(in tableView: UITableView) -> Int {
return tasksArray.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "taskCellRow") as! SwipeTableViewCell
cell.delegate = self
cell.textLabel?.text = tasksArray[indexPath.row].title
return cell
}
func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath, for orientation: SwipeActionsOrientation) -> [SwipeAction]? {
if orientation == .left {
guard isSwipeRightEnabled else { return nil }
let doneAction = SwipeAction(style: .destructive, title: "Done") { (action, indexPath) in
//STEP1: Append the task to the doneTasksArr:
self.tasksArray[indexPath.row].isDone = true
//STEP3: Remove the Row:
self.tasksArray.remove(at: indexPath.row)
//STEP4: Update the Model:
self.saveTasks()
self.delegate?.updateTaskName(name: "")
self.tableView.reloadData()
}
let unDoneAction = SwipeAction(style: .destructive, title: "Undone") { (unDoneAction, indexPath) in
self.tasksArray[indexPath.row].isDone = false
self.tasksArray.remove(at: indexPath.row)
self.saveTasks()
}
//configure btn:
doneAction.backgroundColor = .cyan
unDoneAction.backgroundColor = .blue
if Sege.selectedSegmentIndex == 0 { //Doing this to allow the user to unDone tasks.
return [doneAction]
} else { return [unDoneAction] }
} else {
let deleteAction = SwipeAction(style: .destructive, title: "Delete") { action, indexPath in
self.context.delete(self.tasksArray[indexPath.row])
self.tasksArray.remove(at: indexPath.row)
self.saveTasks()
self.delegate?.updateTaskName(name: "")
self.tableView.reloadData()
}
return [deleteAction]
}
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
}
// MARK: - Class Methods:
#IBAction func addBtnTapped(_ sender: UIBarButtonItem) {
Sege.selectedSegmentIndex = 0 // Doing this to avoid the user to insert a task into the DoneTasks by mistake! and avoiad a bug :)
insertNewTask()
}
func insertNewTask() {
var textField = UITextField()
let alert = UIAlertController(title: "New Task", message: "Please Add Your Task", preferredStyle: .alert)
alert.addTextField { (alertTextField) in
alertTextField.placeholder = "Create New Task"
textField = alertTextField
}
let action = UIAlertAction(title: "Add", style: .default) { (action) in
let newItem = Task(context: self.context)
newItem.title = textField.text!
newItem.isDone = false
newItem.date = Date()
self.tasksArray.append(newItem)
self.delegate?.updateTaskName(name: newItem.title!)
self.saveTasks()
}
alert.addAction(action)
self.present(alert, animated: true, completion: nil)
}
// MARK: - Sege Section:
#IBAction func segeControlTapped(_ sender: UISegmentedControl) {
switch Sege.selectedSegmentIndex
{
case 0:
//Loading normal tasks whose are not done
loadTasks()
case 1:
//Loading the doneTasks:
loadDoneTasksFrom()
default:
print("There's something wrong with Sege!")
}
}
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
let completedTasksToDisply = 0
if Sege.selectedSegmentIndex == 1 {
if let firstTask = tasksArray[section].date {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "dd/MM/yyyy"
let dateString = dateFormatter.string(from: firstTask)
return " " + dateString + " " + " " + "Completed Tasks: \(completedTasksToDisply)"
}
}
return ""
}
override func tableView(_ tableView: UITableView, willDisplayHeaderView view: UIView, forSection section: Int) {
let header: UITableViewHeaderFooterView = view as! UITableViewHeaderFooterView
header.textLabel?.font = UIFont(name: "Verdana", size: 13.0)
header.textLabel?.textAlignment = NSTextAlignment.center
}
//MARK: - Model Manipulation Methods:
func saveTasks() {
do {
try! context.save()
} catch let err {
print("Error Saving context \(err)")
}
}
func loadTasks() {
let request: NSFetchRequest<Task> = Task.fetchRequest()
request.predicate = NSPredicate(format: "isDone == %#", NSNumber(value: false))
request.sortDescriptors = [NSSortDescriptor(key: "isDone", ascending: false)]
do{
tasksArray = try! context.fetch(request)
} catch {
print("There was an error with loading items \(error)")
}
tableView.reloadData()
}
func loadDoneTasksFrom() {
let request:NSFetchRequest<Task> = Task.fetchRequest()
request.predicate = NSPredicate(format: "isDone == %#", NSNumber(value: true))
request.sortDescriptors = [NSSortDescriptor(key: "isDone", ascending: false)]
do{
tasksArray = try context.fetch(request)
} catch {
print("Error fetching data from context\(error)")
}
tableView.reloadData()
}
}
EDIT: Okay I see what you are trying to do. Give this a shot - we will need a double array - each array inside will have to be split by date - our query would look something like this:
var tasksArray = [[Task]]()
var sectionDates = [String]()
func loadTasks() {
tasksArray.removeAll(keepingCapacity: false)
sectionDates.removeAll(keepingCapacity: false)
let request: NSFetchRequest<Task> = Task.fetchRequest()
request.predicate = NSPredicate(format: "isDone == %#", NSNumber(value: false))
request.sortDescriptors = [NSSortDescriptor(key: "isDone", ascending: false)]
do{
let tasks = try! context.fetch(request)
for task in tasks {
if let taskDate = task.date {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "dd/MM/yyyy"
let dateString = dateFormatter.string(from: taskDate)
if self.sectionDates.contains(dateString) {
if let section = self.sectionDates.firstIndex(of: dateString) {
self.tasksArray[section].append(task)
}
} else {
self.sectionDates.append(dateString)
// EDIT - ADD THIS LINE (and self. where necessary)
self.tasksArray.append([])
if let section = self.sectionDates.firstIndex(of: dateString) {
self.tasksArray[section].append(task)
}
}
}
}
} catch {
print("There was an error with loading items \(error)")
}
tableView.reloadData()
}
Could also be helpful to load your objects in order but with this set up it should still group everything correctly regardless of the order - unless you want your sections in a specific order then you can specify in query
Now changes to your tableView delegate methods
override func numberOfSections(in tableView: UITableView) -> Int {
return sectionDates.count
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return tasksArray[section].count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "taskCellRow") as! SwipeTableViewCell
cell.delegate = self
cell.textLabel?.text = tasksArray[indexPath.section][indexPath.row].title
return cell
}
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
let completedTasksToDisply = 0
if Sege.selectedSegmentIndex == 1 {
let dateString = sectionDates[section]
return " " + dateString + " " + " " + "Completed Tasks: \(completedTasksToDisply)"
}
return ""
}
I figured I'd leave the rest of the tableView delegate methods to you but for the most part we just need to identify the section before the indexPath row when working with the double array - as we did in cellForRow
I ran this code and this was the result:
I am showing data like expendable tableview with a header section. Header is showing fine. but When I am showing rows . (Like in a product 5 addons then data is showing like in expendable Tableview Every Row with a Same Type Name Header). I am attaching screenshot. What type of data is showing.
Image Data Showing Like : https://imgur.com/a/JXqUbJD
I am attaching I current code which I am using . I already tried too many times but it is not working fine.
import UIKit
import Reachability
import Alamofire
class ExpendableTableview: UIViewController, UITableViewDelegate, UITableViewDataSource, CollapsibleTableViewHeaderDelegate {
#IBOutlet weak var tableview: UITableView!
var catid:Int!
var reachability = Reachability()!
var arraySubCategory = [structSubCategory]()
struct structSubCategory {
var id:Int
var minimum_people:String
var title:String
var package_price:String
var package_image:String
var package_label:String
var categoryId:Int
// var addons: [cateringAddOns]
// var collapsed: Bool
}
var arraySection = [section]()
struct section
{
var title: String
var addons : [cateringAddOns]
var collapsed: Bool
}
var arrayCateringAddOns = [cateringAddOns]()
struct cateringAddOns {
var no_of_items:Int
var add_on_type:Int
var package_item:String
var upgrade_price:String
}
var NormalArr = ["Balance", "Sales", "Order Status", "Social Media Complaints", "Logout"]
override func viewDidLoad() {
super.viewDidLoad()
print(catid)
SubCateringAPI(cateringId: catid)
// Do any additional setup after loading the view.
}
func numberOfSections(in tableView: UITableView) -> Int {
return arraySection.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return arraySection[section].collapsed ? 0 : arraySection[section].addons.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "CellRID") as! ExpendableAddOnsCell
let item : cateringAddOns = arraySection[indexPath.section].addons[indexPath.row]
cell.lblAddon.text = item.package_item
return cell
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let header = tableView.dequeueReusableHeaderFooterView(withIdentifier: "header") as? CollapsibleTableViewHeader ?? CollapsibleTableViewHeader(reuseIdentifier: "header")
header.titleLabel.text = arraySection[section].title
header.arrowLabel.text = ">"
header.setCollapsed(arraySection[section].collapsed)
header.section = section
header.delegate = self
return header
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 44.0
}
func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
return 1.0
}
func toggleSection(_ header: CollapsibleTableViewHeader, section: Int) {
let collapsed = !arraySection[section].collapsed
// Toggle collapse
arraySection[section].collapsed = collapsed
header.setCollapsed(collapsed)
tableview.reloadSections(NSIndexSet(index: section) as IndexSet, with: .automatic)
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableView.automaticDimension
}
// func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
// return arraySection[section].title
// }
//
#IBAction func btnDismiss(_ sender: Any) {
dismiss(animated: true, completion: nil)
}
func SubCateringAPI(cateringId:Int)
{
if (reachability.connection == .wifi) || (reachability.connection == .cellular)
{
SwiftLoader.show(animated: true)
let url = BaseUrl + ViewController.sharedInstance.subCatering + "cateringId=\(cateringId)"
print(url)
Alamofire.request(url, method: .get, parameters: nil, encoding: URLEncoding.default).responseJSON { response in
switch response.result {
case .success:
let json = response.result.value
print(json)
let code = (json as AnyObject).object(forKey: "code") as! Int
print(code)
if code == 200
{
let data = (json as AnyObject).object(forKey: "data") as? NSArray
for alldata in data!
{
let id = (alldata as AnyObject).object(forKey: "id") as! Int
let minimum_people = (alldata as AnyObject).object(forKey: "minimum_people") as! String
let title = (alldata as AnyObject).object(forKey: "title") as! String
let package_price = (alldata as AnyObject).object(forKey: "package_price") as! String
let package_image = (alldata as AnyObject).object(forKey: "package_image") as! String
let package_label = (alldata as AnyObject).object(forKey: "package_label") as! String
let categoryId = (alldata as AnyObject).object(forKey: "categoryId") as! Int
let arr = structSubCategory(id: id, minimum_people: minimum_people, title: title, package_price: package_price, package_image: package_image, package_label: package_label, categoryId: categoryId)
self.arraySubCategory.append(arr)
}
for i in self.arraySubCategory
{
let id = i.categoryId
let title = i.package_label
self.cateringAddOndsAPI(adonsId: id, title: title)
}
SwiftLoader.hide()
}
else
{
}
case .failure:
print("error")
}
}
}
else
{
alert(title: "", message: "Please Check Your Internet Connection")
}
}
func cateringAddOndsAPI(adonsId:Int, title: String)
{
if (reachability.connection == .wifi) || (reachability.connection == .cellular)
{
arraySection.removeAll()
arrayCateringAddOns.removeAll()
SwiftLoader.show(animated: true)
let url = BaseUrl + ViewController.sharedInstance.cateringAddOnds + "adonsId=\(adonsId)"
print(url)
Alamofire.request(url, method: .get, parameters: nil, encoding: URLEncoding.default).responseJSON { response in
switch response.result {
case .success:
let json = response.result.value
print(json)
let code = (json as AnyObject).object(forKey: "code") as! Int
print(code)
if code == 200
{
let data = (json as AnyObject).object(forKey: "data") as? NSArray
for alldata in data!
{
let no_of_items = (alldata as AnyObject).object(forKey: "no_of_items") as! Int
let add_on_type = (alldata as AnyObject).object(forKey: "add_on_type") as! Int
let package_item = (alldata as AnyObject).object(forKey: "package_item") as! String
let upgrade_price = (alldata as AnyObject).object(forKey: "upgrade_price") as! String
let arr = cateringAddOns(no_of_items: no_of_items, add_on_type: add_on_type, package_item: package_item, upgrade_price: upgrade_price)
self.arrayCateringAddOns.append(arr)
let expandData = section(title: title, addons: [arr], collapsed: false)
self.arraySection.append(expandData)
}
self.tableview.reloadData()
SwiftLoader.hide()
}
else
{
}
case .failure:
print("error")
}
}
}
else
{
alert(title: "", message: "Please Check Your Internet Connection")
}
}
}
you should call override func prepareForReuse() inside the class CollapsibleTableViewHeader.
override func prepareForReuse() {
super.prepareForReuse()
//TODO: set default values
}
Could you provide code of CollapsibleTableViewHeader class?
people, I have this issue when I try back image from different cell
(Thread 1: Fatal error: Index out of range)
what I'm doing here ?
I'm trying to build an Instagram clone and in my home view controller that what should posts show up. I make navigation with a table view and that table view has 2 cell with the different identifier. cell number 1 it's a header that brings data from users table to my username label and profile image. and cell number 2 its for posts its should bring post data like image and caption. I use firebase database.
my code :
import UIKit
import FirebaseAuth
import FirebaseDatabase
class HomeViewController: UIViewController ,UITableViewDelegate {
#IBOutlet weak var tableview: UITableView!
var posts = [Post]()
var users = [UserD]()
override func viewDidLoad() {
super.viewDidLoad()
tableview.dataSource = self
loadposts()
userDetal()
// var post = Post(captiontxt: "test", photoUrlString: "urll")
// print(post.caption)
// print(post.photoUrl)
}
func loadposts() {
Database.database().reference().child("posts").observe(.childAdded){ (snapshot: DataSnapshot)in
print(Thread.isMainThread)
if let dict = snapshot.value as? [String: Any]{
let captiontxt = dict["caption"] as! String
let photoUrlString = dict["photoUrl"] as! String
let post = Post(captiontxt: captiontxt, photoUrlString: photoUrlString)
self.posts.append(post)
print(self.posts)
self.tableview.reloadData()
}
}
}
func userDetal() {
Database.database().reference().child("users").observe(.childAdded){ (snapshot: DataSnapshot)in
print(Thread.isMainThread)
if let dict = snapshot.value as? [String: Any]{
let usernametxt = dict["username"] as! String
let profileImageUrlString = dict["profileImageUrl"] as! String
let user = UserD(usernametxt: usernametxt, profileImageUrlString: profileImageUrlString)
self.users.append(user)
print(self.users)
self.tableview.reloadData()
}
}
}
#IBAction func logout(_ sender: Any) {
do {
try Auth.auth().signOut()
}catch let logoutErrorr{
print(logoutErrorr)
}
let storyboard = UIStoryboard(name: "Start", bundle: nil)
let signinVC = storyboard.instantiateViewController(withIdentifier: "SigninViewController")
self.present(signinVC, animated: true, completion: nil)
}
}
extension HomeViewController: UITableViewDataSource{
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return posts.count
}
func numberOfSections(in tableView: UITableView) -> Int {
return users.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row == 0{
let cell = tableview.dequeueReusableCell(withIdentifier: "imagecell", for: indexPath) as! PostCellTableViewCell
cell.postimage.image = nil
cell.tag += 1
let tag = cell.tag
cell.captionLabel.text = posts[indexPath.row].caption
let photoUrl = posts[indexPath.row].photoUrl
getImage(url: photoUrl) { photo in
if photo != nil {
if cell.tag == tag {
DispatchQueue.main.async {
cell.postimage.image = photo
}
}
}
}
return cell
} else if indexPath.row == 1 {
let cell = tableview.dequeueReusableCell(withIdentifier: "postcell", for: indexPath) as! HeaderTableViewCell
cell.userimage.image = nil
cell.tag += 1
let tag = cell.tag
cell.usernamelabel.text = users[indexPath.row].username
//Error showing here????????????????????????????????????
let profileImageUrl = users[indexPath.row].profileImageUrl
getImage(url: profileImageUrl) { photo in
if photo != nil {
if cell.tag == tag {
DispatchQueue.main.async {
cell.userimage.image = photo
}
}
}
}
return cell
}
return UITableViewCell()
}
func getImage(url: String, completion: #escaping (UIImage?) -> ()) {
URLSession.shared.dataTask(with: URL(string: url)!) { data, response, error in
if error == nil {
completion(UIImage(data: data!))
} else {
completion(nil)
}
}.resume()
}
}
try this one.
cell.tag = indexpath.row
What is the content of users array ?
Are you sure you want to define as many sections as users or as many rows ?
In this case use
func numberOfRows(in tableView: NSTableView) -> Int {
return users.count
}
As explained, you need to rewrite completely cellForRowAt
It should look like this :
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
if row < users.count {
let user = users[row]
if let cellView = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "CellID"), owner: self) {
(cellView as! NSTableCellView).textField?.stringValue = user.name
// do the same for all the fields you need to set
return cellView
} else {
return nil
}
}
return nil
}
thanx, my friend, I found a good way to contain my cell. for post cell, i just use cellForRowAt and but the post data. for header cell i use viewForHeaderInSection
and but my user data with heightForHeaderInSection. to make the high for a view
I am following this tutorial for expanding and collapsing my table view section. As this demo is done in swift 2.2 I have made all the changes according to swift 3.0 . I am stuck at the below function at if condition(currentSectionCells[row]["isVisible"]) which gives me error as "Type 'NSFastEnumerationIterator.Element' (aka 'Any' has no subscript members)'".
func getIndicesOfVisibleRows() {
visibleRowsPerSection.removeAll()
for currentSectionCells in cellDescriptors {
var visibleRows = [Int]()
for row in 0...((currentSectionCells as! [[String: AnyObject]]).count - 1) {
if currentSectionCells[row]["isVisible"] as! Bool == true {
visibleRows.append(row)
}
}
visibleRowsPerSection.append(visibleRows)
}
}
I have tried type casting it as below
func getIndicesOfVisibleRows() {
visibleRowsPerSection.removeAll()
for currentSectionCells in cellDescriptors {
var visibleRows = [Int]()
for row in 0...((((currentSectionCells) as? NSMutableArray)?.count)! - 1) {
let temp = [currentSectionCells][row] as? NSMutableDictionary
let temp2 = temp?["isVisible"] as! Bool
if temp2 == true {
visibleRows.append(row)
}
}
visibleRowsPerSection.append(visibleRows)
}
}
But this gives me a crash at runtime on this line "let temp2 = temp?["isVisible"] as! Bool"
Crash says "EXC_BAD_INSTRUCTION" and the temp shows as nil.
Please help guys. TIA
Table View Delegate and Data Source
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
if cellDescriptors != nil {
return cellDescriptors.count
}
else {
return 0
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return visibleRowsPerSection[section].count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let currentCellDescriptor = getCellDescriptorForIndexPath(indexPath: indexPath as NSIndexPath)
let cell = tableView.dequeueReusableCell(withIdentifier: currentCellDescriptor["cellIdentifier"] as! String, for: indexPath) as! CustomCell
if currentCellDescriptor["cellIdentifier"] as! String == "sectionCellIdentifier" {
if let primaryTitle = currentCellDescriptor["secondaryTitle"]
{
cell.sectionTitleLabel.text = primaryTitle as? String
}
}
else if currentCellDescriptor["cellIdentifier"] as! String == "shortAnswerCell" {
cell.questionTitle.text = currentCellDescriptor["primaryTitle"] as? String
cell.questionTextView.text = currentCellDescriptor["secondaryTitle"] as? String
cell.answerTextView.text = currentCellDescriptor["answerTitle"] as? String
}
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let indexOfTappedRow = visibleRowsPerSection[indexPath.section][indexPath.row]
let temp = cellDescriptors[indexPath.section] as? NSArray
let temp2 = temp?[indexOfTappedRow ] as? NSDictionary
let temp3 = temp2?["isExpandable"] as! Bool
if temp3 == true {
var shouldExpandAndShowSubRows = false
if temp3 == false {
// In this case the cell should expand.
shouldExpandAndShowSubRows = true
}
temp2?.setValue(shouldExpandAndShowSubRows, forKey: "isExpanded")
for i in (indexOfTappedRow + 1)...(indexOfTappedRow + (temp2?["additionalRows"] as! Int)) {
(temp![i] as AnyObject).setValue(shouldExpandAndShowSubRows, forKey: "isVisible")
}
}
getIndicesOfVisibleRows()
tblExpandable.reloadSections(NSIndexSet(index: indexPath.section) as IndexSet, with: UITableViewRowAnimation.fade)
}
I worked on that tutorial as well and completed it successfully in swift3.Your solution is given below modify accordingly.
class yourClass: UIViewController
{
#IBOutlet weak var profileTableView: UITableView!
internal var visibleRowsPerSection = [[Int]]()
internal var cellDescriptors: NSMutableArray!
// VIEW DID LOAD
override func viewDidLoad() {
super.viewDidLoad()
profileTableView.showsVerticalScrollIndicator = false
loadProfileControllerData()
profileTableSetUp()
// Do any additional setup after loading the view.
}
func loadProfileControllerData(){
if let path = Bundle.main.path(forResource: "CellDescriptor", ofType: "plist") {
cellDescriptors = NSMutableArray(contentsOfFile: path)
}
getIndicesOfVisibleRows()
profileTableView.reloadData()
}
// SHOW PARENT VISIBLE ROWS AND SAVE THERE ROW INDEX IN ARRAY
func getIndicesOfVisibleRows() {
visibleRowsPerSection.removeAll()
for currentSectionCells in cellDescriptors.objectEnumerator().allObjects as! [[[String:Any]]]{
var visibleRows = [Int]()
for row in 0..<currentSectionCells.count {
if currentSectionCells[row]["isVisible"] as! Bool == true {
visibleRows.append(row)
}
}
visibleRowsPerSection.append(visibleRows)
print(visibleRowsPerSection)
}
}
// GET REQUIRED OBJECT OF TYPE [String: Any]
func getCellDescriptorForIndexPath(indexPath: NSIndexPath) -> [String: Any] {
let indexOfVisibleRow = visibleRowsPerSection[indexPath.section][indexPath.row]
let cellDescriptorss = cellDescriptors[indexPath.section] as! NSArray
let data = cellDescriptorss.object(at: indexOfVisibleRow) as! [String:Any]
return data
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
//----------------------
// EXTENSION TO OUR PROFILE CLASS THAT DETERMINE OUR CLASS CONFIRM 2 IMPORTANT DELEGATES
extension profileViewController : UITableViewDelegate,UITableViewDataSource{
//MARK-: TABLE VIEW DELEGATE FUNCTIONS
// RETURN NUMBER OF SECTION IN TABLE VIEW
public func numberOfSections(in tableView: UITableView) -> Int{
if cellDescriptors.count != 0{
return cellDescriptors.count
}
else{
return 0
}
}
// RETURN NUMBER OF ROWS IN EACH SECTION OF TABLE VIEWS
public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
return visibleRowsPerSection[section].count
}
/* Return object of UITableViewCell that contains table SECTON data and USER profile data */
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell{
let currentCellDescriptor = getCellDescriptorForIndexPath(indexPath: indexPath as NSIndexPath)
let menuCell = tableView.dequeueReusableCell(withIdentifier: currentCellDescriptor["cellIdentifier"] as! String, for: indexPath) as! yourCellClass
if currentCellDescriptor["cellIdentifier"] as! String == "parent"{
}
else if currentCellDescriptor["cellIdentifier"] as! String == "child"{
menuCell.backgroundColor = UIColor.clear
}
return menuCell
}
public func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath){
let indexOfTappedRow = visibleRowsPerSection[indexPath.section][indexPath.row]
let cellDescriptorss = cellDescriptors[indexPath.section] as! NSArray
var data = cellDescriptorss.object(at: indexOfTappedRow) as! [String:Any]
if data["isExpandable"] as! Bool == true{
var shouldExpandAndShowSubRows = false
if data["isExpanded"] as! Bool == true{
shouldExpandAndShowSubRows = false
(cellDescriptorss[indexOfTappedRow] as AnyObject).setValue(shouldExpandAndShowSubRows, forKey: "isExpanded")
}
for i in (indexOfTappedRow + 1)...(indexOfTappedRow + (data["additionalRows"] as! Int)) {
(cellDescriptorss[i] as AnyObject).setValue(shouldExpandAndShowSubRows, forKey: "isVisible")
}
}
getIndicesOfVisibleRows()
self.profileTableView.reloadSections(NSIndexSet(index: indexPath.section) as IndexSet, with: UITableViewRowAnimation.fade)
}
Thank You for helping me out, I was stuck at a point where the sections weren't expanding even after your help, so just made some changes in the syntax as Swift 3.0 is very specific about type casting hence the didSelectRowAt wasn't functioning properly. Here is the complete didSelectRowAt method. Happy coding.
public func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath){
let indexOfTappedRow = visibleRowsPerSection[indexPath.section][indexPath.row]
if (cellDescriptors[indexPath.section] as! [[String: AnyObject]])[indexOfTappedRow] ["isExpandable"] as! Bool == true {
var shouldExpandAndShowSubRows = false
if (cellDescriptors[indexPath.section] as! [[String: AnyObject]])[indexOfTappedRow]["isExpanded"] as! Bool == false {
// In this case the cell should expand.
shouldExpandAndShowSubRows = true
}
((cellDescriptors[indexPath.section] as! NSMutableArray)[indexOfTappedRow] as AnyObject).setValue(shouldExpandAndShowSubRows, forKey: "isExpanded")
for i in (indexOfTappedRow + 1)...(indexOfTappedRow + ((cellDescriptors[indexPath.section] as! [[String: AnyObject]])[indexOfTappedRow]["additionalRows"] as! Int)) {
((cellDescriptors[indexPath.section] as! NSMutableArray)[i] as AnyObject).setValue(shouldExpandAndShowSubRows, forKey: "isVisible")
}
}
Swift 3/4 without use of NSMutable arrays based on the tutorial and all the code wrapped in a model.
class CellsDescriptorModel {
private var cellDescriptors: [[[String:Any]]]!
private var visibleRowsPerSection : [[Int]]
var CellDescriptors : [[[String:Any]]] { get { return cellDescriptors }}
var VisibleRowsPerSection : [[Int]] { get { return visibleRowsPerSection }}
init(plist:String) {
visibleRowsPerSection = [[Int]]()
if let url = Bundle.main.url(forResource:plist, withExtension: "plist") {
do {
let data = try Data(contentsOf:url)
cellDescriptors = try PropertyListSerialization.propertyList(from: data, options: [], format: nil) as! [[[String:Any]]]
} catch {
print(error)
}
}
getIndicesOfVisibleRows()
}
func getCellDescriptorForIndexPath(indexPath: IndexPath) -> [String: Any] {
let indexOfVisibleRow = visibleRowsPerSection[indexPath.section][indexPath.row]
return cellDescriptors[indexPath.section][indexOfVisibleRow]
}
func expandCell(indexPath:IndexPath) {
let indexOfTappedRow = visibleRowsPerSection[indexPath.section][indexPath.row]
if cellDescriptors[indexPath.section][indexOfTappedRow] ["isExpandable"] as! Bool == true {
var shouldExpandAndShowSubRows = false
if cellDescriptors[indexPath.section][indexOfTappedRow]["isExpanded"] as! Bool == false {
shouldExpandAndShowSubRows = true
}
cellDescriptors[indexPath.section][indexOfTappedRow]["isExpanded"] = shouldExpandAndShowSubRows
for i in (indexOfTappedRow + 1)...(indexOfTappedRow + (cellDescriptors[indexPath.section][indexOfTappedRow]["additionalRows"] as! Int)) {
cellDescriptors[indexPath.section][i]["isVisible"] = shouldExpandAndShowSubRows
}
}
else {
if cellDescriptors[indexPath.section][indexOfTappedRow]["cellIdentifier"] as! String == "DataPickerTableViewCell" {
var indexOfParentCell: Int!
for index in (0..<indexOfTappedRow).reversed() {
if cellDescriptors[indexPath.section][index]["isExpandable"] as! Bool == true {
indexOfParentCell = index
break
}
}
cellDescriptors[indexPath.section][indexOfParentCell]["secondaryTitle"] = ""
cellDescriptors[indexPath.section][indexOfParentCell]["isExpanded"] = false
for i in (indexOfParentCell + 1)...(indexOfParentCell + (cellDescriptors[indexPath.section][indexOfParentCell]["additionalRows"] as! Int)) {
cellDescriptors[indexPath.section][i]["isVisible"] = false
}
}
}
getIndicesOfVisibleRows()
}
private func getIndicesOfVisibleRows() {
visibleRowsPerSection.removeAll()
for currentSectionCells in cellDescriptors {
var visibleRows = [Int]()
for row in 0..<currentSectionCells.count {
if currentSectionCells[row]["isVisible"] as! Bool == true {
visibleRows.append(row)
}
}
visibleRowsPerSection.append(visibleRows)
}
}
}
First I get these values into my Firebase Database, which works successfully.
func handleSale() {
let ref = FIRDatabase.database().reference().child("tickets")
let childRef = ref.childByAutoId()
guard let Price = emailTextField.text, ticketName = passwordTextField.text else {
print("Form is not valid")
return
}
let values: [String: AnyObject] = ["Price": Price, "ticketName": ticketName]
ref.observeEventType(.ChildAdded, withBlock: { (snapshot) in
let ticketId = snapshot.key
let ticksRef = FIRDatabase.database().reference().child("tickets").child(ticketId)
ticksRef.observeSingleEventOfType(.Value, withBlock: { (snapshot) in
guard let dictionary = snapshot.value as? [String: AnyObject] else {
return
}
self.messages.append(Ticket(dictionary: dictionary))
childRef.updateChildValues(values) { (error, ref) in
if error != nil {
print(error)
return
}
print(snapshot)
print(dictionary)
print(ticketId)
}
}, withCancelBlock: nil)
})
}
I then tried to setup a tableview with a custom cell class and fetch these values into the tableview, however being fairly new to swift, I know I haven't done this correctly. In the end I would like that my values "price" and "ticketName" shows up in my table's..
here is my tableview:
import UIKit
import Firebase
class Sales: UITableViewController {
let cellId = "cellId"
var messages = [Ticket]()
var messagesDictionary = [String: Ticket]()
override func viewDidLoad() {
super.viewDidLoad()
var messages = [Ticket]()
var messagesDictionary = [String: Ticket]()
navigationItem.leftBarButtonItem = UIBarButtonItem(title: "Logout", style: .Plain, target: self, action: #selector(heya))
tableView.registerClass(FredericCell.self, forCellReuseIdentifier: cellId)
func fetchTicket() {
FIRDatabase.database().reference().child("tickets").observeEventType(.ChildAdded, withBlock: { (snapshot) in
if let dictionary = snapshot.value as? [String: AnyObject] {
let ticketId = snapshot.key
Ticket.setValuesForKeysWithDictionary(dictionary)
return
}
}, withCancelBlock: nil)
}
print("test to see if it works")
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 5
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier(cellId, forIndexPath: indexPath) as! FredericCell
return cell
}
func heya() {
print ("working")
}
I have edited in hope of filling out my cells, but without luck..:
override func tableView(tableView: UITableView,
numberOfRowsInSection section: Int) -> Int {
return tickets
.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier(cellId, forIndexPath: indexPath) as! FredericCell
let ticket = tickets[indexPath.row]
cell.textLabel?.text = ticket.ticketName
cell.detailTextLabel?.text = ticket.Price
dispatch_async(dispatch_get_main_queue(), {
self.tableView.reloadData()
})
return cell
}