Is there any way to sort rows of a table view using a date/time? swift - swift

I have a messaging function in an app and I'm trying to sort the cells of a tableView based off a timestamp included with every message. I have two strings that are used to populate two different custom cells. Due to this, I can't just rearrange the contents of the string, I need to rearrange the rows themselves.
class messageThreadViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return 2
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if section == 0 {
return finalItems.count
} else {
return finalItems2.count
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.section == 0 {
let cell = tableView.dequeueReusableCell(withIdentifier: "message1") as! MyTableViewCellThread
cell.messagelabel?.text = finalItems[indexPath.row]
return cell
} else {
let cell2 = tableView.dequeueReusableCell(withIdentifier: "message2") as! MyTableViewCellThread2
cell2.messageLabel2?.text = finalItems2[indexPath.row]
return cell2
}
}
the two different strings are finalItems and finalitems2. I have attached an image of the app running to help get a better idea of what I'm trying to do. Let me know if I can explain it better. Thanks in advance!

In general, when sorting cells in tableviews, you simply sort the arrays (datasource) themselves and then call the tableView.reloadData() method.
To sort arrays, the best practice is
let sortedArray = array.filter { $0.timestamp > $1.timestamp }
// This sort with descending order, just replace '>' with '<'

Related

Enable unique actions for each row in UITableView

I have a small tableview of 7 rows. I would like to enable different actions for a press of each row.
For example, the row at index 0 would display another VC (without passing data). The row at index 0 would open a web URL, the next might be a share function, etc.
My biggest problem is the first case of send the user to another VC. Mostly because I don't know if I should enable a segue. And for all cases, I need to be able to specify a function for each row separately, therefore I would need to specify the proper index (0,1,2,etc).
How can I do this is Swift?
Here is a skeleton of code you need:
class ViewController: UITableViewController {
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 2 }
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell()
cell.textLabel?.text = String(describing: indexPath)
return cell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if indexPath.row == 0 {
print("Do action for row 0")
} else if indexPath.row == 1 {
print("Do action for row 1")
}
}
}

Remove index from sidemenu user type wise

I have implemented side menu in tableview and now my scenario is like, I have to manage sidemenu options as user types
Let me show my code
var items = ["Social Media Post", "Messages", "Manage User","My Account","Information","Logout"]
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return items.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell") as! MenuTableViewCell
cell.lblTitle.text = items[indexPath.row]
cell.imgMenuLogo.image = image[indexPath.row]
print(User_type)
if User_type == 2{
items.remove(at: 0)
}
return cell
}
but now i want . to de like this
if user_type == "3"{
// Social Media , Messages And Manage User options i want to remove
}
I am not able to understand how to remove from index.
Try something like this:
override func viewDidLoad() {
super.viewDidLoad()
getList()
}
func getList(){
switch userType{
case 0:
items = ["UserTypeOne_Home","UserType_One Settings","etc"]
break
case 1:
items = ["UserTypeTwo_Home","UserType_Two Settings","etc"]
break
default:
break
}
self.tableView.reloadData()
}
extension ViewController: UITableViewDelegate,UITableViewDataSource{
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return items.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCell(withIdentifier: "Some ID")
cell?.textLabel?.text = items[indexPath.row]
return cell!
}
}
Try not to change the array from within the cellForRowAt indexPath method using indexPath.row, that will not give you the result you want. Modulate the array from outside the protocol method overrides, and just call the reloadData() method.
Try to use enum of UserType and check type of current user than Make an array with default options available to every user and then append specific data in array according to user type. Hope it clarifies :)
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell") as! MenuTableViewCell
cell.lblTitle.text = items[indexPath.row]
cell.imgMenuLogo.image = image[indexPath.row]
print(User_type)
if User_type == 2{
items.remove(at: 0)
}
return cell
}
This will work, But you have done a small mistake here.
You have removed from array after setting label. So you need to remove the item from array first then do set label.
Btw, I will not recommend this method as you need to add/remove from array for every cellForRowAt method.

What's someones preferred method for adding a comments section to a TableViewCell?

I am currently using a tableview to display a individual "Posts." In each tableview cell is a unique Post. I would like to add a collection of "comments" into each cell. The only way I can think about collecting comments is to add another TableView inside the Post's cell. What is some preferred methods to accomplishing this? It seems pretty complicated to use a TableView inside another TableViews Cell.
I don't think it's a good idea.
Create N sections in your table view, one section for every post.
For every section
the cell at row 0 will be populated with the post data
every cell in the following rows will be populated with the comments data.
Example
struct PostModel {
let title: String
let comments: [String]
}
class Posts: UITableViewController {
private var posts: [PostModel] = ... // TODO
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return posts.count
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return posts[section].comments.count + 1
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let post = posts[indexPath.section]
let cell = tableView.dequeueReusableCellWithIdentifier("StandardCell") ?? UITableViewCell(style: .Default, reuseIdentifier: "StandardCell")
if indexPath.row == 0 {
cell.textLabel?.text = post.title
return cell
} else {
let comment = post.comments[indexPath.row-1]
cell.textLabel?.text = comment
return cell
}
}
}

swift 2 how to remove empty rows in a tableview in a viewcontroller

Is there a way to reset tableview height so that no empty rows are showed. For an example, a tableview displays 3 rows but there is only one row having real data. I'd like the tableview shrinks it size so there is only one row display
I guess you have a View Controller like this
class Controller: UITableViewController {
private var data: [String?] = ["One", nil, "Three", nil, nil]
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return data.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("MyCellID")!
cell.textLabel?.text = data[indexPath.row]
return cell
}
}
Since your model (data in my example) contains nil values you are getting a table view like this
One
_
Three
_
_
Removing the empty rows
Now you want instead a table view like this right?
One
Three
Filtering your model
Since the UI (the table view) is just a representation of your model you need to change your model.
It's pretty easy
class Controller: UITableViewController {
private var data: [String?] = ["One", nil, "Three", nil, nil]
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return data.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("MyCellID")!
cell.textLabel?.text = data[indexPath.row]
return cell
}
override func viewDidLoad() {
data = data.filter { $0 != nil }
super.viewDidLoad()
}
}
As you can see, inside viewDidLoad() I removed the bill values from the data array. Now you'll get only cells for real values.
I think you need remove the empty data before execute func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int.
//EDITED
I give you this possible solution, maybe there are more ways to do it, you can try with this.
class MyViewController: UITableViewDataSource, UITableViewDelegate {
private var realHeight = 0
private var data : [String] = []
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("MyCellID")!
cell.textLabel?.text = data[indexPath.row]
self.realHeight += self.tableView.rowHeight //Increase real height value.
return cell
}
func loadData() { //This function reload the data
self.realHeight = 0
self.tableView.reloadData()
//Update tableViewHeight here, use self.realHeight. You can use constraints or update directly the frame.
}
}
The solution is have a counter that represents the sum of all visibles rows height. e.g If you have n cells then your height will be self.tableView.rowHeight * n, then you ever will have an effective height.
I recommend you to create a constraint for the table height and when the cells change, you only need change the constant of the constraint, is more easy than change the frame.

How to show only filtered data in a table

How to show the data thats decks.status == true, and ignore those objects set to false?
data:
var decks: [DeckOfCards]
What I've got now:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as TableViewCell
if (thedeck.decks[indexPath.row].status == true) {
cell.label.text = "\(thedeck.decks[indexPath.row].card.name)"
}
}
You're going about this the wrong way. By the time you get to cellForRowAtIndexPath, you're already stated that a cell should be dequeued for this index path (and therefore at this index in your data array). The right place to be doing this filtering is in your data source.
For example, in addition to your decks array, you could make a computed property (filteredDecks) that gets its value by filtering the decks array.
var decks = [DeckOfCards]
var filteredDecks: [DeckOfCards] {
return decks.filter { $0.status }
}
You can then use this property as the data source for your table view.
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return filteredDecks.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath)
cell.label.text = "\(filteredDecks[indexPath.row].card.name)"
return cell
}
Now since this solution computes the filteredDecks array on each property access, it may not be the best approach if decks is a large array, or if you're reloading the table view frequently. If this is the case, and it's possible to do so, you should prefer filtering the decks array ahead of time using the same method shown in the computed property above.
You could use the filter function on the decks
let filteredDecks = decks.filter({$0.status})
Filter your array as
self.decks = self.decks.filter {
(d: DeckOfCards) -> Bool in
return d.status == true
}
Now your array will have the filtered values. you dont need to check for status inside cellForRowAtIndexPath function.