Background - I have a custom UITableViewCell layout. Each cell will have a number of UILabels with a variable number of rows of UILabels. For the sake of argument conside 3 columns of UILabels (different widths), and a variable number of rows depending on the data.
To best allow for content view size changes (e.g. edit mode, change in orientation etc) I was going to manually layout each of the UILabels in the cell in the "layoutSubviews" method, effectively setting up their exact positions. I thought this way they will be laid out appropriately for events such as EDIT mode, orientation change etc.
Question - I'm wondering whether from a performance point this is the best approach? Or should I be looking at somehow predefine the UILabel rows, one predefined set for portrait mode and another set for landscape mode? (not exactly sure of how this would be done, but I'm trying to describe an approach where the layout would not have to be re-calculated when orientation changes etc)
thanks
suggestion by RickMaddy elsewhere was good:
"Why have a variable number of rows of labels in a single table row? Have one row of labels per table row. Then the problem is easier and performance won't be an issue."
Related
I was asking here about the option to make a collectionView with multiple cells since our cells have a different appearance for different indexs.
The answer was not sufficient because other then just create multiple cells and pick one for every index, you have all sorts of problems such as :
How can you register multiple classes ? collectionView.register(FlowViewCell.self
Reusable cells system gets crazy because they are different
Every action you do on a cell must be identified with its class and it becomes a mess.
So, I am searching for another way to do so with a single cell.
What would be a good way to set different buttons in the same position of a cell, and show/enable a different button with different indexes ?
So for index 1 I have 2 circle buttons, and for index 2 I have one large wide button? if you just put them on the same spot and hide one you get a strange design.
The answer you linked is what you are looking for.
We use 4 different types of cell in our app and we don't have any problem with it.
You don't have to register multiple classes, you can all create them in your storyboard (inside the collectionview), the system will register the classes for you.
And then you call the cells like explain in the answer.
The hard part is having different sizes, we only have two (large cell and square cell, so two square cells fit on the same level), more than that would be tricky, especially different height.
But apart from that you can have different content on each cell without any problem.
I would like to build a TableView where when the user clicks on a cell it expands to show more information. My question is how do I use Autolayout to arrange the multiple items in each cell.
Each cell will always be the same size, whether it is collapsed or not, so the sizing isn't dynamic.
The first problem I have is how to use Autolayout to arrange all the items in the cell. Before Xcode 7 I was successfully using Autolayout where I would pick a label-button-view to arrange, click Editor > Align > Trailing/Leading/Top Space, to.. etc. This is now greyed out and I don't know how to replace my old strategy.
Each cell has two rows of items. The first row shows all the time, the second only shows on collapse. Below is a picture of how the cell will look when it is collapsed:
The first row is a bit trickier because outlet1 and outlet 2 will have variable sizes. I would like 'label' to come right after label1, no matter how long or short that outlet happens to be. As arranged currently, there is a variable amount of space between the two.
What I'm looking to achieve in row 1 is basically exactly like Venmo:
Notice how 'paid' conforms to the size of the two names in the first row.
The second row has two buttons and an outlet which will always be the same size.
To sum up - how can I layout these elements in the UITableView for iphones4 thru 6S - and then how do I make this cell a collapsible cell? The construction of these cells seem to work as a system, not isolated from the whole - which is why this is a 2-part question.
is this what you want to achieve?
I have the following:
I would like to make it so that the tableview stops right at the "So" cell, instead of having all the blank cells under it (the big plan is to have a bunch of multiple choice questions like the one in that pic, all on top of one another on one scrollview). I'm guessing I need to set the tableView's frame, but I was wondering if there was an easy way to calculate at runtime the exact height of the portion of the tableview where the cells are implemented (the ones with text in the pic). It's tricky because I made it so that each cell's height can change to accommodate the amount of text in it. Anyone have any advice?
There are two appproaches you can take.
Best and easiest.
1. Add a footer view to the table so it will not draw the rows after the last row. a blank footer view will do.
//Add empty view to hide trailing row seperators
UIView *emptyView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 0)];
emptyView.backgroundColor = [UIColor whiteColor];
[tableView setTableFooterView:emptyView];
[emptyView release];
2.Check how many rows you have in the tableview and calculate the tableheight and change the frame of table view.
There isn't a tableView:sizeOfAllCells: function if that is what you are looking for. From your question it sounds like you to adjust the size of your table so you can put other content on bottom of it.
You are correct is saying that you want to alter your UITableView's frame property to match the height.
[table setFrame:CGRectMake(0,0,table.frame.size.width,heightOfCells)];
There are a few things you could do though. If you had an array of all the cells, you could loop through it real quick and add the height of each of them. You could (I don't particularly recommend it though) loop though the number of sections and rows you have, using tableView:cellForRowAtIndex: instead of having an array of cells, and get the heights that way. If each cell is the same height (sounds like its possible it wont be) you could just do some simple math to figure out the height. Lastly, you could keep a dictionary or array of heights and update it in the tableView:cellForRowAtIndex: so if the content changes, it updates the size, but then you would have to call a reloadData. Those are just a few ideas of ways to solve this particular problem.
Here's a hack (so wait to see if a better solution comes along):
Use numberOfSections to get the number of sections (if you later change to grouped style), and then add up the heights of rectForSection: for each of the sections (in plain style, just take rectForSection:0). Maybe add a bit to this for the separators.
I thought I had read that for a UITableView that heightForRowAtIndexPath doesn't get called on all rows, but only on the ones that will be visible. This isn't what I'm seeing however. I'm seeing hundreds of calls to heightForRowAtIndexPath for the simple situation of the orientation being changed of the iPhone for example.
So I'm assuming here therefore that for a UITableView with heightForRowAtIndexPath implemented, it does (i.e. heightForRowAtIndexPath) get called for all rows (not just the visible ones)...let me know if this isn't quite correct.
QUESTION: Given the above, how many rows in a UITableView (where heightForRowAtIndexPath is implemented) can you have before performance issues occur typically?
Is there a way around the performance issues? i.e. set a nominal/standard height for each row and not implement heightForRowAtIndexPath, but then correctly set each row height only when it is displayed and set it correctly here...but which method would one do this in?
Have a look at the discussion section in the tableView:heightForRowAtIndexPath: documentation
The method allows the delegate to specify rows with varying heights. If this method is implemented, the value it returns overrides the value specified for the rowHeight property of UITableView for the given row.
There are performance implications to using tableView:heightForRowAtIndexPath: instead of the rowHeight property. Every time a table view is displayed, it calls tableView:heightForRowAtIndexPath: on the delegate for each of its rows, which can result in a significant performance problem with table views having a large number of rows (approximately 1000 or more).
So you should use the rowHeight property of the UITableView. If you need different heights you are out of luck because you have to use tableView:heightForRowAtIndexPath:.
AFAIK there is no way to change the row height at display.
The tableview has to know the correct size before, otherewise there would be ugly position shifts all the time.
I think I found a solution to that.
In iOS 7 apple introduced some new tableview properties. One of them is the:
tableView:estimatedHeightForRowAtIndexPath:
So if you supply an estimated row height, for example, then when tableView:heightForRowAtIndexPath: is called repeatedly before the table is displayed, it is called only for the visible cells of the table; for the remaining cells, the estimated height is used.
Here is the source for that information: https://books.google.gr/books?id=wLaVBQAAQBAJ&pg=PT521&lpg=PT521&dq=heightforrowatindexpath+only+for+the+visible+cells&source=bl&ots=7tuwaMT5zV&sig=h3q8AaFvoCgcrPu2fQchVkIEjwg&hl=en&sa=X&ved=0CEMQ6AEwBWoVChMInLK0xbPuxwIVCbUaCh3_nQWG#v=onepage&q=heightforrowatindexpath%20only%20for%20the%20visible%20cells&f=false
My goodness, I spent over an hour trying to find the source of my performance problem!
Finally I also found hundreds of calls to heightForRowAtIndexPath and a search
got me this thread. THAT is really annoying.
Performance goes down here already when just displaying 250 items. Thankfully the cells I want to display now all have the same size. But I could imagine someone wanting to display some different cells for a tableView with > 200 items!
FIX THIS APPLE!
Cheers
A way to improve performance in tableViews with a big number of rows and dynamic cell heights is to cache the height of the cells once they are first calculated.
A simplistic approach to achieve this is to keep a NSMutableDictionary in which the key is the id of the record in the cell (or any other identifier you might have), and the value is a NSNumber with the height of the row. Once the height is first calculated, store it the NSMutableDictionary by the record id. In the tableView:heightForRowAtIndexPath and tableView:estimatedHeightForRowAtIndexPath: you check for a cached height in the dictionary and return it if found. If not found, calculate the height, and store in the cache before returning the height.
You might have to be careful with invalidating the cache for the rows that change heights. For example, if you have an expand button in one of your cells, you will need to remove the height of that cell from cache once the expand button is tapped delegate method for height is called.
You might still have a performance hit if you try to display 1000 of cells at once when the table shows, as it will likely call the height method for each row. A work around for that is to first warm the cache, if possible in a background task, before first displaying the cells.
I have this interface with multiple UILabels.
On view loading i populate white labelled values with some data from a db.
The problem is, some of that fields are potentially too long for the interface, so i'd like to compute the total height of one label once the text is word wrapped and reposition the 2 labels below (shifting the Y coordinate) accordingly to the previous label's height.
All of this should go inside a UIScrollView to let the user scroll those labels vertically.
Any chance i can do this easily with some control i still don't know, or do i have to do it manually?
Thanks
You'll need to use the NSString UIKit Additions to compute the height you need to set on your UILabel, and then adjust the other controls appropriately.
Specifically, I think you want to use sizeWithFont:forWidth:lineBreakMode: to get the rect for your UILabel.
Alternatively, you could use a UIWebView and display the information as HTML. I don't know if it's necessarily less work, but you'll get a layout that automatically adjusts to the size of its contents.