How to make an Expandable TableView with Profile Img? - ios

I was trying to create a Slide side out menu using the SWRevealViewController but I could not do the expandable TableView and the profile with image + Hello, Name...
And all I could do was:
Basically how do you make an expandable TableView and the profile.
Are there any examples of how to do this?

From what I can tell, you want an expandable table view, ie: Click a row in the table, and more options will appear.
I've achieved that in the past using the following:
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
if let cell = tableView.cellForRowAtIndexPath(indexPath) as? OuterCell{
if let object = data[indexPath.row] as? OuterObject{
if(object.open == false){
data.insert(InnerObject(content: object.innerContent), atIndex: indexPath.row + 1)
tableView.beginUpdates()
tableView.insertRowsAtIndexPaths([NSIndexPath(forRow: indexPath.row + 1, inSection: 0)], withRowAnimation: .Left)
tableView.endUpdates()
}else{
data.removeAtIndex(indexPath.row + 1)
tableView.beginUpdates()
tableView.deleteRowsAtIndexPaths([NSIndexPath(forRow: indexPath.row + 1, inSection: 0)], withRowAnimation: .Left)
tableView.endUpdates()
}
object.open = !object.open
}
}
}
Basically what is happening here is the dataSource for the TableView is represented by the data array in this example. This is what you connect to all the TableViewDelegate hooks.
Within the data array I am holding two types of objects, a OuterObject and an InnerObject.
The OuterObject contains all the information for the inner objects that it contains, and if you click on it, it will dynamically add its children into the data array.
Example Data (I'm using JSON cause its easy to read and understand, you'll have to represent this in swift)
[
{
name="Ensino",
type="outerObject",
open=true
childObjects=[
{
name="Servico",
type="innerObject"
},
{
name="Avaliacao",
type="innerObject"
}
]
},
{
name="Pesquisa",
type="outerObject",
open=false
childObjects=[]
}
]
So in your case you would need an "Ensino" object, with a children property containing data for: Avaliaco, Servicos, ect...
Outer Object also keeps state as to whether or not it is open or closed, that way if you click on it again, you can know to remove all the inner objects that belong to it.
In this example I have hardcoded the OuterObject to always have at most one child, you would have to modify the code to be smarter as to how many objects are inserted/deleted upon click to be based on how many children objects the outer object you click has.
By calling tableView.beginUpdates(), tableView.beginUpdates(), tableView.insertRowsAtIndexPaths() you are accessing the methods that will allow an animated transition to happen when you update the data.
The key to solving this problem is creating an intelligent data source, and then leveraging the methods that Apple has in UITableView to make it happen.
Let me know if you need more help, this is an advanced TableView topic.

check this sample code dropdown menu
this might give you some idea.

Related

swift iOS filter on array containing uitableviews

func convertPointToIndexPath(_ point: CGPoint) -> (UITableView, IndexPath)? {
if let tableView = [tableView1, tableView2, tableView3].filter({ $0.frame.contains(point) }).first {
let localPoint = scrollView.convert(point, to: tableView)
let lastRowIndex = focus?.0 === tableView ? tableView.numberOfRows(inSection: 0) - 1 : tableView.numberOfRows(inSection: 0)
let indexPath = tableView.indexPathForRow(at: localPoint) ?? IndexPath(row: lastRowIndex, section: 0)
return (tableView, indexPath)
}
return nil
}
So i got this method, which converts a CGPoint into the indexPath of the given uitableView. I struggle with the filter-Method on the array which contains uitableViews.
I got an array outside of this method which contains any number of uitableViews. For example:
public var littleKanbanColumnsAsTableViews: [UITableView] = []
So i got to make a change inside of the method. Like this:
if let tableView = littleKanbanColumnsAsTableViews.filter({ $0.frame.contains(point)}).first { ... }
Now when i click on any tableView on the gui, i track the coordinates of the point and transform it on the belonging tableview with the frame.contains(point) method.
My problem is that the filter is not working, it always gives me the first tableView back, no matter which tableview is clicked. Why it doesn't work with my littleKanbanColumnsAsTableViews-Array?
One hint:
let tableView = littleKanbanView.littleKanbanColumnsAsTableViews[3]
When i indexing its direct then it works. But i want it depending on which tableView is containing the clicked point.
Here is my array with the tableViews, in this case the array contains 5 tableviews.
array containing tableviews
Now i want to filter the tableView out of them, which includes the point from tapping on this tableView. How can i achieve this?
For more understanding, i add the ui, here is it:
UI of my app
When i click on this tableView it works, because it is the first element in my array of tableViews. So for this case the convertPointToIndexPath-Method is working.
But when i scroll horizontally to the second tableView for example and click on that, it doesn't work. Because I think the method gives me always the first element back, but i thought it filters it with the given condition.
What is the problem, why doesn't work the condition{ $0.frame.contains(point)}? It have to localize the tableView when the coordinates of the point are tracked.
Preferred Solution:
In this case the moment the first satisfying condition is met, the rest of the elements are not traversed.
if let tableView = littleKanbanColumnsAsTableViews.first(where: { $0.frame.contains(point) }) {
}
Not so efficient solution:
In this case all the elements in the array are traversed to build an array of table views that satisfy the condition. Then the first element of that filtered array is chosen.
if let tableView = (littleKanbanColumnsAsTableViews.filter{ $0.frame.contains(point)}).first {
}

Swift UITableView - How to put new items on top not on the bottom of the list?

I am new in Swift. The sample codes about UITableView show that new item is placed on the bottom of the list. How may we reverse this? I searched internet but could not find an answer.
Thanks.
Guven
UITableviews show data based on the order of an array such as an array of characters like
var data = ["B","C","D","E"]
Typically, you add data into array by using append which adds data at the end of the array, hence why it adds it at the bottom of the list.
data.append("A")
If you want your table view to add data on top of the list, then you can add your data at the beginning of the array like this.
data.insert("A", at: 0)
now reload your tableView, and new data would be added at the top of the list
yourTableViewName.reloadData()
To put new item on top, insert it at desired position (index 0) and reload corresponding indexPath (row: 0, section: 0).
let indexPathOfFirstRow = NSIndexPath.init(forRow: 0, inSection: 0)
yourArray.insert("some element", atIndex: 0)
tableView.beginUpdates()
tableView.insertRowsAtIndexPaths([indexPathOfFirstRow], withRowAnimation: .Automatic)
tableView.endUpdates()
Reloading whole table is a costly task and not recommended.
Add item to the array last index .
Then reload tableView
All you really need to do is to .insert your object at index 0 rather than append. Appending your latest value at the first index 0 ensures your last data is shown at the top. You can continue using tableview.reloaddata()as usual. Hope the screenshot helps
Inserting new items on data array at 0 index is easy but keeping the state of tableview as it is difficult especially when you are dealing with headers too...
I am using this to append old messages when paginating the chat...
you can iterate on data and insert new items and header with data.insert("new message", at: 0) but tableview automatically going to jump on zero index. to keep that state you should need a unique id in every new item in my case it was time and messageId
I am saving the last message id before appending new items ... and using that id to calculate the indexPath of the previous top message cell and scroll to it with no animation...
func getIndexPath(WithMessageId id: Double) -> IndexPath? {
for (section, sectionObjects) in messagesArray.enumerated() {
if let index = sectionObjects.Messages?.firstIndex(where: { $0.MessageID == id }) {
return IndexPath(row: index, section: section)
}
}
return nil
}
this method returns the indexpath use that as follows
self.tableView.reloadData()
self.tableView.scrollToRow(at: getIndexPath(WithMessageId: lastMessageID) ?? IndexPath(row: 0, section: 0),
at: .top, animated: false)
its gonna append new items without even any glitches like Twitter appending new news feeds on top...

Must I keep my own UITableView "isSelected" array for multiselect?

I am trying to use a UITableview with multiple selection on and a check mark accessory view for selected rows. This is mostly working if I turn on and off the accessory view in tableView:didSelectRow.
However, I tried to build a selectAll method, and I found that the array of selected cells was being cleared after I had spun through all the cells and selected them if I then call reloadData().
I suspect reloading the table clears selection. I don't know of any other way to have all the cells drawn after I set the selected flag and accessory view.
I am wondering if I need to keep my own array of selected rows. Has anyone else built something like this? Its seems like a common scenario.
Any tips or sample code appreciated.
Take an Array and add the indexPath of each selected cell into it and put a condition in cellForRowAt... that if the Array contains that particular indexPath, set it as selected.
There are two approaches you can take. One is to track the selected row numbers. To do this, you can use an NSMutableIndexSet or its Swift counterpart IndexSet.
Essentially, when a row is selected, add it to the set. When you deselect it, remove it from the set. In cellForRowAtIndexPath you can use containsIndex to determine if a check mark should be shown or not.
Since you explicitly mention an issue with selection when you reload the table, it is worth considering the issue with storing row numbers (whether in a set or an array), and that is that row numbers can change.
Say I have selected rows 4,7 and 9 and these values are stored in the index set. When I reload the data, new data may have been inserted after the "old" row 8, so now I should be selecting rows 4,7 and 10, but I will be selecting 4,7 and 9 still.
A solution to this is to store some sort of unique identifier for the data that should be selected. This will depend on your data, but say you have a string that is unique for each item. You can store this string in a NSMutableSet or Swift Set, which again makes it easy to check if a given item is selected using contains
you need add some functionality in cellForRowAtIndexPath method like this ang your view controller code like this
let we take one example of photo gallery application
class CreateEvent: UIViewController,UITableViewDataSource,UITableViewDelegate {
var yourArray : [Photo] = [Photo]()
//MARK: - Content TableView Methods
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("CustomCell", forIndexPath: indexPath)
let objPhoto = yourArray[indexPath.row]
if objPhoto.isPhotoSelected == true
{
cell.accessoryType = .Checkmark
}
return cell
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath)
{
let objPhoto = yourArray[indexPath.row]
objPhoto.isPhotoSelected = true
let cell = tableView.cellForRowAtIndexPath(indexPath)
cell.accessoryType = .Checkmark
}
//MARK: Action Method
func selectAllPhoto()
{
for objPhoto in yourArray
{
objPhoto.isPhotoSelected = true
}
tableView.reloadData()
}
}
and one more thing you need to create your custom object like
class Photo: NSObject {
var photoName:String = ""
var isPhotoSelected = false
}
hope this will help you
The best approach for multiple selection is
Take a model object, in that take all your attributes and one extra boolean attribute (like isSelected) to hold the selection.
In case of selecting a row
Fetch the relevant object from the array
Then update the isSelected boolean (like isSelected = !isSelected) and reload table.
In case of select all case
Just loop through the array.
Fetch the model object from array.
make the isSelected = true.
After completion of loop, reload the table.

Loading coredata for two collection views and one table view in swift

So I have this issue where I have to load one entity on two collection views, and one table view. The thing is, primarily I just had to load one entity on the VC where the tableview is located. And that data entity has these parameters for i.e.:
Engine type (diesel or petrol)
Car color
Max.velocity
Year of production
Now, the main point was, to color the cells on the tableview depending on the type of the car engine. So, if the data Entity "Car" has a Bool value of "isEngineDiesel" = true, then the cell would be orange, if false, then it would be light blue. And this worked just fine, a simple if statement on the table view delegate method for loading such cells. But, now I had to implement another VC which has two collection views, in which, the first one loads ONLY Diesel engine Car entity's, and the other Petrol type.
So I guess the issue is already clear here. How can I accomplish this? Because after countless hours of experimenting the only idea I had was two make TWO entity's in which the one is DieselCar and the other PetrolCar. But that means changing the complete data structure, and also, instead of one table view I would actually need two, which doesn't seem like a good idea due the fact that it would "overflow" with all the data there is.
So...any ideas gents?
EDIT:
So far I've only managed to get the cell titles, but the return value of number of cells is still a mystery on how to solve.
The Code for collection view delegate:
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("cell", forIndexPath: indexPath) as! CollectionViewCell
if let carData = fetchedResultsController?.objectAtIndexPath(indexPath) as? Car {
cell.layer.cornerRadius = 20
cell.layer.masksToBounds = true
if collectionView == collectionViewDiesel {
if carData.isEngineDiesel == true {
cell.backgroundColor = UIColor(netHex: 0x8DF060)
// Display the cell name
cell.cellTitle.text = carData.cellTitle
}
}else if collectionView == collectionViewPetrol {
if carData.isCarDiesel == false {
cell.backgroundColor = UIColor(netHex: 0xEB9B2D)
// Display the cell name
cell.cellTitle.text = carData.cellTitle
}
}
}
return cell
}
And this is the method I need answered:
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 5
}
On the other VC you need data structure for two collection views so I think make a dictionary from your data base on the isEngineDiesel if true add objects in one array and if false in another array then set those arrays for the respective diesel and petrol keys .
In you collection view delegate there always a UICollectionView argument place check on that (you can use tags or even == operator if you have outlet) to load the data from dictionary base on keys .

Pass table cells textlabel data to array in swift

I want to pass table cell's textLabel data of UITableViewController to NSArray. Those cell have identifier name Cells and accessory type checkmark
Code that does exactly what you asked is here:
func getCellsData() -> [String] {
var dataArray: [String] = []
for section in 0 ..< self.tableView.numberOfSections() {
for row in 0 ..< self.tableView.numberOfRowsInSection(section) {
let indexPath = NSIndexPath(forRow: row, inSection: section)
let cell = self.tableView.cellForRowAtIndexPath(indexPath)!
if cell.reuseIdentifier == "Cells" && cell.accessoryType == UITableViewCellAccessoryType.Checkmark {
dataArray.append(cell.textLabel!.text!)
}
}
}
return dataArray
}
But I would like to recommend you find different approach, because this is the rude traverse of tableView. You probably have your dataSource model that can give you ll data you need. Additionally, this code doesn't check for errors, for example if there is no text in cell at some indexPath
Two things I'd advise.
First, looks like you're using the checkmark accessory to indicate multiple selections. It's really intended for single selection, like a radio button. Better to use the allowMultipleSelectionsoption on the tableview. This will allow...
...the second thing. Copying text from cells into an array is the wrong way round to do it. Better to ask the table view and call it's - (NSArray *)indexPathsForSelectedRows this will give you an array of index paths to selected cells then you can ask for each cell and grab any data you want from it. This gives you better live data and prevents you unknowingly creating a circular reference from the view controller of the tableview and the content in the tableview cells.

Resources