Hide cell from UITableView - swift

I'm trying to hide cells from a UITableView. My codes are below.
When I open the app I see empty rows in my TableViewas you can see here
How can I hide or remove(not delete) empty cells from UITableView?
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! FeedTableViewCell
let row = self.items[indexPath.row]
cell.lblTitle.text = row.title
cell.isHidden = !checkCurrentUser(email: row.email)
return cell
}
I added filtered array but then I take different error like this. My new codes are below. How can I solve this problem?
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! FeedTableViewCell
let row = self.items[indexPath.row]
self.items = self.items.filter{checkCurrentUser(email: $0.email)}
cell.lblTitle.text = row.title
//cell.isHidden = !checkCurrentUser(email: row.email)
return cell
}
Whole codes are below
import UIKit
import Firebase
class OyuncularVC: UIViewController, UITableViewDelegate, UITableViewDataSource {
var items = [ItemModel]()
#IBOutlet weak var tblView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
tblView.tableFooterView = UITableViewHeaderFooterView()
retrieveItems()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.items.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! FeedTableViewCell
let row = self.items[indexPath.row]
self.items = self.items.filter{checkCurrentUser(email: $0.email)} //bunu ekledim siliceksem bunu silicem aga
cell.lblTitle.text = row.title
//cell.isHidden = !checkCurrentUser(email: row.email)
return cell
}
/* Retriev Items */
func retrieveItems() {
DataService.dataService.ITEM_REF.observe(.value, with: { (snapshot: DataSnapshot?) in
if let snapshots = snapshot?.children.allObjects as? [DataSnapshot] {
self.items.removeAll()
print(snapshots.count)
for snap in snapshots {
if let postDic = snap.value as? Dictionary<String, AnyObject> {
let itemModel = ItemModel(key: snap.key, dictionary: postDic)
print(itemModel)
self.items.insert(itemModel, at: 0)
}
}
self.tblView.reloadData()
}
})
}
func checkCurrentUser(email: String) -> Bool {
let currentUser = Auth.auth().currentUser
return email == currentUser?.email
}
}
}

If you want to display only the emails of the current user what don't you filter the items in the database (applying a predicate) which is the most efficient way.
Or filter the items in the for snap in snapshots loop.
However if you want to keep the entire data set declare a second array
var items = [ItemModel]()
var filteredItems = [ItemModel]()
and replace
for snap in snapshots {
if let postDic = snap.value as? Dictionary<String, AnyObject> {
let itemModel = ItemModel(key: snap.key, dictionary: postDic)
print(itemModel)
self.items.insert(itemModel, at: 0)
}
}
with the following it performs the check in the loop
let currentUser = Auth.auth().currentUser
self.filteredItems.removeAll()
for snap in snapshots {
if let postDic = snap.value as? Dictionary<String, AnyObject> {
let itemModel = ItemModel(key: snap.key, dictionary: postDic)
print(itemModel)
self.items.insert(itemModel, at: 0)
if itemModel.email == currentUser?.email {
self.filteredItems.insert(itemModel, at: 0)
}
}
}
And replace also the two data source methods with
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return filteredItems.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! FeedTableViewCell
let row = self.filteredItems[indexPath.row]
cell.lblTitle.text = row.title
return cell
}
And delete the method checkCurrentUser

Related

Sectioning UITableView Cells - Repeating Cells

I'm trying to section my Tableview data based on a Key in my Firebase database.
I'm able to section everything properly based on the key (itemPreset).
I'm having trouble assigning the reusable cells to their sections.
The cells keep repeating themselves with the same text value in each cell.
The amount of rows per cell is correct and the section header title is correct.
Here is my code -
var subCategories = [SubCategoryCellInfo]()
var sectionsArray = [String]()
func querySections() -> [String] {
for selection in subCategories {
let subCategory = selection.itemPreset
sectionsArray.append(subCategory ?? "")
}
let uniqueSectionsArray = Set(sectionsArray).sorted()
return uniqueSectionsArray
}
func queryItemPreset(section:Int) -> [Int] {
var sectionItems = [Int]()
for selection in subCategories {
let itemPreset = selection.itemPreset
if itemPreset == querySections()[section] {
sectionItems.append(querySections().count)
}
}
return sectionItems
}
func numberOfSections(in tableView: UITableView) -> Int {
return querySections().count
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return querySections()[section]
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if isFiltering(){
return filtered.count
}
return queryItemPreset(section: section).count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let subCell = tableView.dequeueReusableCell(withIdentifier: "subCell", for: indexPath) as! SubCategoryTableViewCell
let section = queryItemPreset(section: indexPath.section)
let task = section[indexPath.row]
let sub: SubCategoryCellInfo
if isFiltering(){
sub = filtered[task]
}
else{
sub = subCategories[task]
}
subCell.nameOfLocationText.text = sub.itemPreset
return subCell
}
SubCategoryCellInfo:
class SubCategoryCellInfo{
var itemPreset: String?
init(itemPreset:String?){
self.itemPreset = itemPreset
}
}
Solution:
I grouped the array into sections based on itemPreset and then used that section
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let subCell = tableView.dequeueReusableCell(withIdentifier: "subCell", for: indexPath) as! SubCategoryTableViewCell
let groupedDictionary = Dictionary(grouping: subCategories) { (person) -> String in
return person.itemPreset ?? ""
}
var grouped = [[SubCategoryCellInfo]]()
let keys = groupedDictionary.keys.sorted()
keys.forEach { (key) in
grouped.append(groupedDictionary[key]!)
}
let task = grouped[indexPath.section]
let sub: SubCategoryCellInfo
if isFiltering(){
sub = filtered[indexPath.row]
}
else{
sub = task[indexPath.row]
}
subCell.nameOfLocationText.text = sub.itemPreset
return subCell
}
Inside your SubCategoryTableViewCell write this code.
override func prepareForReuse() {
super.prepareForReuse()
nameOfLocationText.text = nil
}
Solution: Group the array into sections based on itemPreset and then use that section.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let subCell = tableView.dequeueReusableCell(withIdentifier: "subCell", for: indexPath) as! SubCategoryTableViewCell
let groupedDictionary = Dictionary(grouping: subCategories) { (person) -> String in
return person.itemPreset ?? ""
}
var grouped = [[SubCategoryCellInfo]]()
let keys = groupedDictionary.keys.sorted()
keys.forEach { (key) in
grouped.append(groupedDictionary[key]!)
}
let task = grouped[indexPath.section]
let sub: SubCategoryCellInfo
if isFiltering(){
sub = filtered[indexPath.row]
}
else{
sub = task[indexPath.row]
}
subCell.nameOfLocationText.text = sub.itemPreset
return subCell
}

Use 2 Table View in the same View Controller (Swift 4)

I have a problem handling 2 tables on the same screen. Every time he keeps crashing. Can someone help me?
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell: TableViewCellComunicazioni?
if tableView == self.tableViewNotifica {
cell = tableView.dequeueReusableCell(withIdentifier: "cellNotifica", for: indexPath) as? TableViewCellComunicazioni
let dataNotifica = structNotifica[indexPath.row].dateNotifica
let testoNotifica = structNotifica[indexPath.row].textNotifica
cell?.dateNotification.text = "\(date!)"
cell?.textNotification.text = "\(text!)"
return cell!
}
if tableView == self.tableViewInbox {
cell = tableView.dequeueReusableCell(withIdentifier: "cellInbox", for: indexPath) as? TableViewCellComunicazioni
let email = structInbox[indexPath.row].email
let messaggio = structInbox[indexPath.row].messaggio
let data = structInbox[indexPath.row].data
cell?.emailInbox.text = "\(email!)"
cell?.messaggioInbox.text = "\(message!)"
cell?.dataInbox.text = "\(date!)"
return cell!
}
return UITableViewCell()
}
This could be a probable fix for your problem:
Coding Example:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if tableView == self.tableViewNotifica {
let cell = tableView.dequeueReusableCell(withIdentifier: "cellNotifica", for: indexPath) as? TableViewCellComunicazioni
let dataNotifica = structNotifica[indexPath.row].dateNotifica
let testoNotifica = structNotifica[indexPath.row].textNotifica
cell?.dateNotification.text = "\(date!)"
cell?.textNotification.text = "\(text!)"
return cell!
}
if tableView == self.tableViewInbox {
let cell = tableView.dequeueReusableCell(withIdentifier: "cellInbox", for: indexPath) as? TableViewCellComunicazioni
let email = structInbox[indexPath.row].email
let messaggio = structInbox[indexPath.row].messaggio
let data = structInbox[indexPath.row].data
cell?.emailInbox.text = "\(email!)"
cell?.messaggioInbox.text = "\(message!)"
cell?.dataInbox.text = "\(date!)"
return cell!
}
return UITableViewCell()
}
And make sure that you have correct cell identifier for dequeuing.
extension ViewController : UITableViewDelegate,UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return listmoviesArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if tableView == moviesTableView {
let cell = tableView.dequeueReusableCell(withIdentifier: "MovieTableViewCell", for: indexPath) as! MovieTableViewCell
cell.delegate = self
cell.setupCell(listmoviesArray[indexPath.row],indexPath: indexPath)
return cell
} else {
let cell = tableView.dequeueReusableCell(withIdentifier: "MovieTableViewCell2", for: indexPath) as! MovieTableViewCell
cell.delegate = self
cell.setupCell(listmoviesArray[indexPath.row],indexPath: indexPath)
return cell
}
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if tableView == moviesTableView {
// Handle your selection for row.
} else {
//Handle your selection for row.
}
}
}
Above code produces the following output with 2 Tableview.

Cell casting throws exception

I am trying to load information to a tableView, and I get an exception because some information in the cell isn't initialized when I cast to it. and this is my code :
The code for the view containing the tableView:
private func populateActiveChats()
{
let loggedOnUserID = Auth.auth().currentUser?.uid
let ref = Constants.refs.databaseChatsLite.child(loggedOnUserID!)
ref.observe(.value, with:
{ (snapshot) in
for child in snapshot.children.allObjects as! [DataSnapshot]
{
if (self.chatsDictionary.keys.contains(child.key) == false)
{
let chatValueDictionary = child.value as? NSDictionary
self.AddChatToCollections(chatAsDictionary: chatValueDictionary)
self.DispatchQueueFunc()
}
}
})
}
func AddChatToCollections(chatAsDictionary: NSDictionary!)
{
if chatAsDictionary == nil
{
return
}
let contactName =
chatAsDictionary[Constants.Chat.ChatRoomsLite.CONTACT_NAME] as! String
let newMsgs = chatAsDictionary[Constants.Chat.ChatRoomsLite.NUM_OF_UNREAD_MSGS] as! Int
let contactID = chatAsDictionary[Constants.Chat.ChatRoomsLite.CONTACT_ID] as! String
let chatToAdd = PrivateChatLiteObject(chattingWith: contactName, ContactID: contactID, unreadMessages: newMsgs, LastMSG: "")
chatsDictionary[contactID] = chatToAdd
chatsIndex.append(contactID)
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let cell = ChatsTableView.dequeueReusableCell(withIdentifier: "chat_room_cell", for: indexPath) as! PrivateChatUITableViewCell
let indexedID = chatsIndex[indexPath.row]
cell.ContactName.text = chatsDictionary[indexedID]?.GetContactName()
cell.ContactID = chatsDictionary[indexedID]?.GetContactID()
return cell
}
And in my PrivateChatUITableViewCell:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let cell = ChatsTableView.dequeueReusableCell(withIdentifier: "chat_room_cell", for: indexPath) as! PrivateChatUITableViewCell
//cell.ContactImageView = UIImageView.loadImageUsingUrlString(contactImg)
let indexedID = chatsIndex[indexPath.row]
cell.ContactName.text = chatsDictionary[indexedID]?.GetContactName()
cell.ContactID = chatsDictionary[indexedID]?.GetContactID()
//cell.PopulateCell()
return cell
}
public func PopulateCell()
{
let currentID = Constants.refs.currentUserInformation?.uid
Constants.refs.databaseChatsLite.child(currentID!).child(ContactID!).observeSingleEvent(of: .value, with: {(snapshot) in ...})
}
The code crashes when it reaches the Constants.refs.databaseChatsLite.child(currentID!).child(ContactID!)
line because ContactID isn't initialized.
This is being called when casting cell to PrivateChatUITableViewCell
I haven't changed my code and this used to work, so I am not sure what changed or what I am doing wrong. Where should my code be fixed?

Cannot invoke initializer for type 'Double' with an argument list of type '([Int])'

I'm converting the timestamp to normal type but I'm getting this error --- Cannot invoke initializer for type 'Double' with an argument list of type '([Int])', How to fix it?
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "jobCell", for: indexPath)
//print jobTime = 1504753200
let unixTimestamp = Double(jobTime) //error here
let date = Date(timeIntervalSince1970: unixTimestamp)
cell.textLabel?.text = "\(jobTime[indexPath.row])-\(jobEndTime[indexPath.row])"
return cell
}
I'm retrieving the JSON data and put into table view cell
var jobTime = [Int]()
var jobEndTime = [Int]()
viewDidLoad
guard let jobs = json["jobs"] as? [[String:Any]] else {return }
for job in jobs {
if let id = job["jobTime"] as? Int{
self.jobTime.append(id)
}
if let id = job["jobEndTime"] as? Int{
elf.jobEndTime.append(id)
}
}
self.tableView.reloadData()
}
Try this-
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "jobCell", for: indexPath)
//print jobTime = 1504753200
let unixTimestamp = Double(jobTime[indexPath.row]) //Change here
let date = Date(timeIntervalSince1970: unixTimestamp)
cell.textLabel?.text = "\(jobTime[indexPath.row])-\(jobEndTime[indexPath.row])"
return cell
}

Maximum one checkmark in TableView in Swift

I would like that users can choose maximum one voice. And that the checkmark jumps to where you tapt and deselect the other.
It looks like very simple, but I don't see the solution. And I can't find the answer on the internet.
Please can anybody help me?
Thanks advance!
import UIKit
import AVFoundation
class voicesTableViewController: UITableViewController {
fileprivate let synthesizer = AVSpeechSynthesizer()
fileprivate var speechVoices = AVSpeechSynthesisVoice.speechVoices()
override func viewDidLoad() {
super.viewDidLoad()
}
// MARK: - Table view data source
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 speechVoices.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
//Name
let voice = speechVoices[indexPath.row]
let voiceLang = voice.language as? String
let theVoice = UserDefaults.standard.object(forKey:"voice") as? String
cell.textLabel?.text = voice.name
// Language
if let language = countryName(countryCode: voice.language) {
cell.detailTextLabel?.text = "\(language)"
}
else {
cell.detailTextLabel?.text = ""
}
cell.detailTextLabel?.textColor = UIColor.gray
// Checkmark
if (theVoice != nil) {
if(theVoice == voiceLang) {
cell.accessoryType = UITableViewCellAccessoryType.checkmark
}
}
return cell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let voice = speechVoices[indexPath.row]
if tableView.cellForRow(at: indexPath)?.accessoryType == UITableViewCellAccessoryType.checkmark {
tableView.cellForRow(at: indexPath)?.accessoryType = UITableViewCellAccessoryType.none
}
else
{
//if ((tableView.indexPathsForSelectedRows?.count)! > 1) {
tableView.cellForRow(at: indexPath)?.accessoryType = UITableViewCellAccessoryType.checkmark
//}
}
UserDefaults.standard.set(voice.language, forKey:"voice")
UserDefaults.standard.synchronize()
tableView.deselectRow(at: indexPath, animated: true)
}
func countryName(countryCode: String) -> String? {
let preferredLanguage = NSLocale.preferredLanguages[0] as String
let current = Locale(identifier: preferredLanguage)
return current.localizedString(forLanguageCode: countryCode) ?? nil
//return current.localizedString(forIdentifier: indentifier) ? nil
}
}
Simple change of function cellForRow:atIndexPathshould work:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
//Name
let voice = speechVoices[indexPath.row]
let voiceLang = voice.language as? String
let theVoice = UserDefaults.standard.object(forKey:"voice") as? String
cell.textLabel?.text = voice.name
// Language
if let language = countryName(countryCode: voice.language) {
cell.detailTextLabel?.text = "\(language)"
}
else {
cell.detailTextLabel?.text = ""
}
cell.detailTextLabel?.textColor = UIColor.gray
// Checkmark
if(theVoice != nil && theVoice == voiceLang) {
cell.accessoryType = .checkmark
} else {
cell.accessoryType = .none
}
return cell
}
UPD#1
But you can use better solution:
1) Add property fileprivate var selectedIndexPath: IndexPath?
2) Change function func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath)to next one:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
//Name
let voice = speechVoices[indexPath.row]
let voiceLang = voice.language as? String
let theVoice = UserDefaults.standard.object(forKey:"voice") as? String
cell.textLabel?.text = voice.name
// Language
if let language = countryName(countryCode: voice.language) {
cell.detailTextLabel?.text = "\(language)"
}
else {
cell.detailTextLabel?.text = ""
}
cell.detailTextLabel?.textColor = UIColor.gray
// Checkmark
cell.accessoryType = self.selectedIndexPath == indexPath ? .checkmark : .none
return cell
}
3) And then in func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) you can do next:
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let voice = speechVoices[indexPath.row]
self.selectedIndexPath = indexPath
UserDefaults.standard.set(voice.language, forKey:"voice")
UserDefaults.standard.synchronize()
tableView.deselectRow(at: indexPath, animated: true)
tableView.reloadRows(at: [indexPath], with: .automatic)
}