iOS Dynamic cell content - ios

I have searched for this a lot and haven't found what I am looking for.
I want to have UITableViewCell such that each have dynamic content. For example, one cell can have a place name, address, phone number, image, etc. Another cell can have some or all of these attributes.
I know that I should have functions cellForRowAtIndexPath: and heightForRowAtIndexPath: return stuff appropriately.
The problem I am facing is that I have my cells designed in nib files, and am loading the layout from there. I can calculate the heights for each cell based on conditions(though this is tedious), also I need to arrange stuff in that cell if one thing is missing. For example, if place name is missing then move all the elements up via setFrame:.
There should be an easy way out I believe?

In such dynamic cases I would not bother with using storyboards to accomplish this. It should be very easy to do this programatically...
If you really do need to create this in a storyboard you could generate a full cell which could consist of (for instance) 5 labels. Now in the table view data source I presume you have some array of objects where each element represents a single row in the table view. If so then in your case each of this object can have a method that will return an array of non-empty strings (the strings that will actually be displayed) which can be a dynamical count from 1 up to 5 strings.
If you do this you will request this array in method requesting the height for row at index path and by using this count you can then return the height of the row (for instance numOfStrings*40.0f). Inside the method requesting your cell you can simply fill those labels with the same array you use to calculate the height.
As for non storyboard approach I suggest you to override an UITableViewCell, generate a static method that will return you the cell height for string count and generate an initializer with the string array which should then iterate through the array generating the labels and setting the text to it.

Related

Setting Content on 3 Separate Static TableViewCells that are of the same type

I have a tableview where a single prototype cell is used only 3 times. This cell has some buttons on it that trigger displaying and hiding content within the cell.
I could reuse the cells, but that requires a lot of resetting each cell so that if I expand A's content, B's content isn't expanded when it loads. Furthermore, this requires me to keep a state record in the Controller class, where I would prefer to have this all handled by the Cell itself for modularity. In other words, the amount of work to keep each cell in the correct stage seems inefficient.
What would be the best way to go about this? Do I use static cells? Is there a way to instance 3 separate cells of the same type and place them in the TableView?
You certainly can have the modular part of the code you need inside the cell itself. If you don't want to reuse the cells, then add a custom initializer with a case for each cell type. Inside your delegate method cellForRow:
return CustomCell(type: .myCustomType)
If you do it this way, you can add a switch inside the cell's initializer & set up the cells according to their types that way. And i'm assuming by "static" you mean just three instances. You "add" cells to the table by telling the delegate the number of cells that it will need, which will in turn call cellForRow x times.

How to distinguish 3 objects in a custom cells

I am making an iPhone app.
In this application I have to make a look like below.
I am not allowed to use collection view.
I am using tableview, and custom cells. Which I am easily able to incorporate. Means taking 3 subviews in Custom cells. And making a look.
Here the problem is In a cell, how do I distinguish each object. so that I can call each object, to set an image on image view.
Is there any Object oriented mechanism to distinguish all 3 objects in a cell ?
Try to get the data as NSArray of NSDictionary containing an array of 3 objects that you want to display on cell.
Assign tag to UImageView in the custom cell.
In cellForRowAtIndexPath, get the 3 objects and apply image using switch case.
Well everything depends on how you get the data from the server
You can use outlet collections which will give you an array of UIImageView and you can assign different tags to the imageview so you can assign to them.
You can use these guides to understand how outlet collections works:
http://nshipster.com/ibaction-iboutlet-iboutletcollection/
http://useyourloaf.com/blog/interface-builder-outlet-collections/
All the standard procedures should work to achieve this but then it only depends on how nice you want to do this.
The straight forward procedure is to expose the outlets of the image views and labels in the cell and assign the correct values to those when dequeuing/creating table view cell.
The first upgrade would be to rather expose 3 setters on the cell to simply set your model to each of them which will then internally set the images and texts inside the cell.
The next thing you may do is to rather insert an array of objects (always sending up to 3 in your case) instead of having 3 setters.
At this point you may actually rather use a collection view INSIDE the cell and make the cell a data source for the collection view. But this is totally optional.
Now since you may still dislike the table view data source you may create another model which contains an array of objects (again up to 3 in your case) and make a system that will distribute your original array of objects into the array of these containers.
If then you need to handle buttons or other touch events they may be handled with collection view delegate or 3 buttons and in both cases I advise you to handle those in the cell and create a custom delegate for the cell which will report the event with the appropriate model.
This together generates the following:
When you receive the data call a container class to distribute your array of objects (into groups of 3) and assign it to your table view data source (view controller usually)
Number of rows is the same as number of containers in the array
Cell for row assigns the container with row index to the cell. It assigns self as a delegate
Cell internally handles the object distribution either via collection view, separated outlets or outlet collections.
Cell handles actions and reports them back to the delegate (- (void)myCell:(MyCell *)cell selectedItem:(MyObject *)item;)
The cell delegate can again handle what to do upon reported actions
Also if you want to avoid a collection view inside the cell you can create a custom view using xib so you do not copy the labels, image views and such. Then simply create 3 of these custom views inside the cell. Also by using inspectable and designable these views will be visible inside the storyboard.
First I'll say that a restriction against using UICollectionView is silly. Are you still targeting ios5?
I'd look at it like this.
make your own view class for the 'subcell' let's use this term for any single instance of the 3 views per cell. I'd subclass UIImageView, adding the label for the name down the bottom and a 'setSelected:' kind of method to highlight when selected via user interaction by drawing differently.
make a UITableViewCell subclass to host and layout up to three of these subcells. I say up to 3 because the last cell may contain 1 or 2 subcells and not 3 if the total people to represent is not divisible by three.
Selection Logic: You'll need to override 'setSelected:' because you want to deselect and select only subcells, you don't want the whole cell to highlight on selection, only a third of it.
You'll also want to implement touchesEnded: in this cell so that you can figure out which of the three subcells was last touched, and you'll need to be able to query or communicate this back to the controller, probably using delegation. If the cell can communicate back whether selection was in subcell 0,1 or 2 then this together with the UITableViewDelegate didSelectAtIndexPath should map to your model nicely - selectedPerson = myArrayOfPeople[ (indexPath.row * 3) + subcellIndex ]
You'll be able to decorate your cells in cellForRowAtIndexPath: in similar fashion..
personOne = model.arrayOfPeople[indexPath.row*3]
personTwo = model.arrayOfPeople[indexPath.row*3 +1 ]
personThree = model.arrayOfPeople[indexPath.row*3 + 2 ]

iOS Swift How to Extract Strings from All Selected Rows in a TableView

I want to loop through a TableView and extract the text from all the selected rows. I suppose I "could" create and maintain a special array that is updated every time a row is selected/deselected using the didSelect/didDeselectRowAtIndexPath methods. But creating a separate array seems like an extra step. Is there no way to let the TableView itself serve as the array and then simply loop through it and get the selected rows? What would the code look like? I'm new to Swift, so this might be a silly question.
Part of the problem is that cells are supposed to be reused, and when used this way it is not possible to loop through them all. You could get around this by using a unique reuse identifier for each cell, such as the indexPath itself or some underlying unique id in your model. Then, you could indeed loop through all cells and retrieve whatever state you desired from each.
You would, however, find your application crushed under the weight of too many cells being instantiated and kept in memory. If you don't have many cells you won't be killed, but try it with a big data set and your app will enjoy a very quick death.
It is far more efficient to store one array with a bunch of id's than a large number of memory-intensive UITableViewCells.
As mentioned in comments, you should work with underlying datasource, not the table itself.
For example if your table shows rows from Array, it is way more faster to retrieve strings directly from that array than creating UITableViewCells and get strings from them.
Get indices of selected rows using UITableView's property indexPathsForSelectedRows.
Query datasource for each row.
As has been said the tableview only handles displaying, your datasource is what powers the data shown if you think about it.
Plus as said before the tableview dequeues cells as they scroll on and off the screen.
The best way to achieve what you want is to add a property to your datasource for each element that will allow you to filter out the select properties easily.
How are you storing the state for each selected cell currently? As this is the same functionally you would use to be able to generate your selected text array.

iOS 7: Two different heights for cells in a table inheriting from the same UITableViewCell

I need "Two different heights for cells in a table inheriting from the same UITableViewCell".
A bit more on this. I am using iOS 7 and storyboard. I created on the story board two different UITableViewCell prototype for a UITableView with custom cells.
I then created a class, MyUITableViewCell which defines the beheaviour of the cell as well as a protocol method that is then implemented by the delegate (which is in my case is the UITableViewController class where the UITableView containing the cells is).
Now.. I would like to dynamically set the row of the cells according to whether the cells is of type 1 or type 2.
I have found this method:
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
// if(indexPath) refers to cell of type 1 then
// return 240;
// else return 120;
// I created a NSDictionary with data that includes the type of cell information, however I am not sure how to link indexPath to the dictionary. The keys of the dictionary are ids and not array indexes.. so I am a bit lost in here..
}
As said in the comment:
"I created a NSDictionary with data that includes the type of cell information, however I am not sure how to link indexPath to the dictionary. The keys of the dictionary are ids and not array indexes.. so I am a bit lost in here.."
I would like to find a solution to this but not sure if it is possible using only NSDictionary or if I need some other hack...
Sure you can do that. The cells in your table view are data backed, you must have access to the data that defines the table in the table view delegate.
If your table is simple with only one section then you might only need to look at indexPath.row. There must be some way to relate this value to your data, if there is not you will not be able to populate the cell in cellForRowAtIndexPath either.
Often the indexPath.row value can be used as an index into an array containing data, such as [tableDataArray objectAtIndex:indexPath.row]; In your case, if the data is a dictionary, what are the keys? Maybe you can make a key directly from the row ([NSNumber numberWithInteger:indexPath.row], since a key must be an object) or maybe you need to use an array to translate the NSNumber produced from the index paths into the keys that are used in your dictionary. If your data is pre-existing and it would be a lot of work to change it this might be the best way, otherwise think about organising your data for easy access.
An alternative would be to use a UICollectionView, where with a custom UICollectionViewLayout you can define the frame of every cell individually at the time you report attributes for the cell. However you still need a way to relate index path to the underlying data. It is more versatile than a UITableView and is possibly a more useful skill to develop with the state of iOS development today.

"Compact" view in iOS

I have a view in iOS (iPhone) that have multiple components, organized in sort of a stack way (one in top of the next). Those are user account properties, some could be blank.
So, I have in my view the components layout like this:
UITextField1 (Name)
UITextField2 (Location)
UITextField3 (Age)
UITextView1 (Bio)
UITableView (user entries).
Some of the fields could be blank. Instead of having blank spaces for the blank fields I would like the next field to move upper.
This is like this question of flowlayout: What is the best/easiest way to create 'flow layout' type layout in iOS.
I can only see two ways of dealing with this:
Creating a function that traverse all the UIViews and determines which ones are blank and move the following upper.
Creating a UITableView and use different cell heights for cells whose content is empty.
Ideally there would be a component, but I cannot find it (basically some sort of stack/flow layout).
Anyways, I believe that I am going to implement the option #1 above, but I don't know if there is an "standard" way of accomplishing this (I honestly don't even know the proper term to look for this feature).
Thanks.
I would lean more in the direction of your second choice by using a UITableView but not the way you propose.
This would be my approach using a UITableView:
Create a UITableViewCell (or custom cell) for each one of my
components and assign a tag value to each, we'll use these later. You can do this in viewDidLoad.
Add code in numberOfRowsInSection to check to see which fields have
data values present. Return the total count of the number of fields
with data values
In cellForRowAtIndexPath again, check if data exists for that field
If so, check to see if the cell created in step one has already been created or not (if not, create)
If not, increment a counter of some sort to increase your tag value and find the next field that has a value. Once found, use that "tag/index" number to "return" the proper cell.
In the end, you have a UITableView only displaying the fields with data.

Resources