I have a paged UICollectionView, with one cell per page. However, my UICollectionViewCells are slow to render (third party library, no options there). So as an optimisation, i need to pre-render the cells to the left and right of the current page, so scrolling is smooth.
What i've done is, inside my custom UICollectionViewLayout, is outset the passed rect by 1 point before checking to see which cells' UICollectionViewLayoutAttributes should be returned, see here:
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect {
CGRect outsetRect = CGRectInset(rect, -1, 0);
return [_myAttributes filter:^BOOL(UICollectionViewLayoutAttributes *attribs) {
return CGRectIntersectsRect(attribs.frame, outsetRect);
}];
}
This works as expected. However, when the actual UICollectionView does its thing, it seemingly ignores the offscreen cells and calls cellForItemAtIndexPath only for the first cell, not the cell to the right (or left).
Any ideas what is going on? Thanks
For future reference, i solved this by:
Making cells for pre-rendering forcibly moved on-screen a little, but with the same size.
Changing their z-index to -1 so they are under the main page cell.
In the layoutAttributesForElementsInRect, check if the rect is two pages wide, and if so then it's scrolling between pages, so don't grab any extra pages
In my cell's applyLayoutAttributes, i check if the z-index<0, and if so, then this is a pre-rendering cell.
Cheers
Related
EDIT: I have create a very small app that represents perfectly the problem. https://drive.google.com/file/d/0B6sI4Feh1HJUb3pGa2pBUmY4QW8/view?usp=sharing
In the sample app, we just need to scroll down then press the button on the top bar to see the problem I am having
I am using https://github.com/jamztang/CSStickyHeaderFlowLayout to have sticky headers behaviors (like default UITableView) inside my collection view.
It works pretty well when scrolling through the collection view. I have a search bar outside the collectionview that allows users to filter the data with search text, everytime the user enters a letter, I'm refreshing the collectionview's data with the found data.
The problem is let's say there is currently 4 sections inside the collection view and that it is scrolled completely at the bottom. When I input a certian letter, it filters out everything but a single item (with a single header). The content size then changes for the collection view and displays the proper data, but then the header is too low (see screenshot).
I have investigated inside the flowlayout and inside the layoutAttributesForElementsInRect and I see it actually set the frame's origin Y to 0 (like it should be), but it seems the collectionview doesn't use this value and uses the previous one (which was when the collection view was scrolled down).
Any idea what could cause the UICollectionView to not use the desired frame inside layoutAttributesForElementsInRect ?
I tried to fix this issue, but it looks like it's impossible till UICollectionViewLayout mechanism is black box for us. Custom layout correctly returns updated frame to layout engine on reloadData:
But hidden engine not even asking attributes if they are equal. In your case you can use simple workaround. Just update contentOffset after reload.:
[self.collectionView reloadData];
self.collectionView.contentOffset = CGPointZero;
I am using a library (RATreeView)
I have a perfectly working table that can expand the way I want it to, but I have the requirement to have different heights on certain sections, this seems to cause the table's height to not change in some way (regarding the scrollable surface of the UIView its added to).
So when I expand sections they allow me to scroll further down, that still works, but I am unable to see all the cells; as if the UIView or table is unable to scroll all the way. The cells differentiating heights are no problem, that comes out perfectly. I also can confirm that it does not have to do with the tab bar being on top of the content either.
- (CGFloat)treeView:(RATreeView*)treeView heightForRowForItem:(id)item {
SomeObject* levelObject = item;
if (levelObject.isChild == YES) {
if ([levelObject.type isEqualToString:#"fatty"]) {
return 140;
}
return 50;
}
return 60;
}
As you can see I set the height based on the type of the object, and whether or not it is supposed to be a section header (a child or parent). So this seems to break the RATreeView and so far unfortunately I cannot solve it. Maybe someone else has.
If any more detail is required I can elaborate.
When initialising subviews onto a view, always ensure the height you are working with is that which you think it is. I made a simple mistake that I assumed that I was working with the height of the ViewController I was currently on when in fact self.view.frame.size.height was giving me the height of the entire CollectionViewController (The entire screen). So just deduct the size of the TabBarController and the height of the ViewControllers that are also on the screen and viola, you have the correct height.
I'm using a nav bar orchestrated set of tableviewcontrollers to display a hierarchy of data. The bottom level of data is displayed in a custom tableviewcell, which has its content described in a xib, and results in a required cell height larger than the default. I posted another question on how to programmatically extract the resulting cell height, but no usable answers, so I now implement heightForRowAtIndexpath to return a hardcoded value that is the height in the xib attributes panel. But, when the table is displayed with more rows than can fit in the normal display size (480h,320w), the bottom row is chopped off as expected, but I can't scroll it to see the rest of the row. I've searched rather extensively, but nothing has helped. I poked at various attributes (tableview sizes, scrollingEnabled, etc.) but they all seem as expected values (e.g., scrollingEnabled is true). One post suggested that no scrolling would occur unless the contentSize was larger then the frame, so I looked at those values, and it sounds promising, in that my frame size is a typical 460h x 320w, but, my contentSize is 0 x 0! Further searching (e.g., "setting contentSize" or "contentSize is 0") didn't clear anything up. I thought contentSize was computed for free from the table row count and their heights, so how could I be getting 0? Even going back to allowing a default row height by not implementing heightForRowAtIndexpath still resulted in a 0 contentSize. Some searches suggest turning off autoLayout for the custom table cell, but still the same. So, what might be going awry? Thanks for any thoughts or guidance.
Update: Sorry, I confess! I had originally been developing on a mac mini with a regular USB mouse, where scrolling in the simulator was left button down and drag. I recently switched to an iMac with the magic mouse. I'm now so used to just dragging my finger on the mouse to scroll code, web pages, etc., that I didn't think to try clicking and holding the left mouse "button", and then dragging the mouse. When I did, everything worked as expected. I had really thought things were hopeless when any similar variables/values in the app on the mini were equal to those on the iMac, but it was just me getting tricked by fancy technology. Sorry for the distraction.
set your tableView's frame and your cell's frame correct,and implement
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath in your UITableView to give a right height. If that doesn't work try set your tableView.autoresizingMask to UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight;
which can help you with the size of the tableview
I had a similar problem. My tableView would't scroll when the content exceeded the tableViews frame.
The problem for me was that I had "Use Autolayout" checked but no constrains on the tableView. Adding vertical and horizontal space constrains on the tableView fixed my problem
Can someone please give me a hint on how to recreate the scrolling effect used in the UltraVisual iPhone app? Here's a gif to illustrate the effect:
The first "cell" is full height while the other displayed cells are regular sized. While the user scrolls up, the first cell slowly animates to the regular height, while the next one slowly gets bigger. Do they use an UITableView? Or an UIScrollView? I have no idea how it's made...
Ha, you made my day! I actually wrote that view :)
This is actually very straightforward. This view uses UICollectionView with a custom UICollectionViewLayout.
The general principle is this. I make up a 'drag interval' – that is the required distance to drag between each cell. This value is arbitrary but affects how much the user has to drag to switch cells. The total height of the collection view is the 'drag interval' * the number of items in the view. Then I set the layout to automatically paginate to the nearest drag interval (which gives it the snapping behavior). This is very similar to how coverflow works. From this you can calculate the index of the 'top cell' by dividing the contentOffset.y by the height.
With the 'top cell' index you can generate the frames for each cell pretty easily. The top cell's frame is { 0, contentOffset.y, 320, 176 }, and from there you can calculate the next cells frame and so forth.
Then the last trick is calculating the interpolation of the page index. This is basically the decimal part of the current cell index. This will give a number between 0 and 1 that can be used to calculate the interpolation between the top frame and the frame below.
Every 'prepareLayout' calculates the frames of the cells on screen, and then in layoutAttributesForElementsInRect:, generate all the layoutAttributes based on the generated frames.
Using this trick you can create all sorts of complicated layouts. UICollectionView can be a powerful beast, but definitely takes a bit to wrap your head around it.
It's very cool! We made a fairly simple to use control like this that can be found here:
https://github.com/RobotsAndPencils/RPSlidingMenu
I've been struggling with this problem for a couple of days now and I can't seem to find any concrete solution online, so here it goes...
The scenario is simple: I want the table view to be expanded (i.e. not scrollable) in a scroll view, and therefore I need to resize and move the view(s) within the scroll view. This I have achieved quite easy by sub classing the UIScrollView and re-implemented the layoutSubviews method (see below).
Implementation of the layoutSubViews:
- (void) layoutSubviews
{
[super layoutSubviews];
//Resize the UITableView containing all the rows (i.e. it should not scroll within the tableview)
UITableView *bt = [self.subviews objectAtIndex:0];
//1 - Set the height of the table cells
//2 - Calculate the total height for the tableview (i.e.: numberOfRows*rowHeight)
bt.frame = CGRectMake(bt.frame.origin.x, bt.frame.origin.y, bt.frame.size.width, ([bt numberOfRowsInSection:0]*bt.rowHeight));
//Move down the note text view, so that it don't overlaps the table.
UITextView *note = [self.subviews objectAtIndex:1];
note.frame = CGRectMake(note.frame.origin.x, (bt.frame.origin.y + bt.frame.size.height)+15, note.frame.size.width, note.frame.size.height);
//Set the content size to the scroll view.
//Note: If the note is hidden, then we should not include it (the same goes for the padding between note and table)
[self setContentSize: CGSizeMake(self.contentSize.width, bt.frame.size.height + note.frame.size.height)];
UIEdgeInsets insets = UIEdgeInsetsMake(0.0f, 0.0f, /*padding#bottom*/50.0f, 0.0f);
[self setContentInset:insets];
[self setScrollIndicatorInsets:insets];
}
The implementation above solves my problems and renders perfectly. Try to think of it as rendering an recipe where each row in the table view is an ingredient - that is what I'm aiming for.
My app is utilizing the UITabBar and everything renders and behave fine except for the case when I scroll down a bit in the scroll view and then switch to another tab and back. The scroll view is then somehow altered and it is no longer possible to scroll to the top (depending on how much you've scrolled down before switching tab) and is also rendered somewhat strange.
Step 1: Step1.png (see URL below)
Scrolled down to be able to see the textview below expanded tableview
Step 2: step2-switched-back.png (See URL below)
Switching to Second tab and back to First, causing odd rendering behavior and scrolling behavior where it is no longer possible to reach the first row in tableview by scrolling the scroll view.
I've created an example project since I believe the code talks for itself, and I hope someone out there can see through this and point out if I've done something wrong or if there are any way to get round this.
Project & screenshots available at: http://eddiex.se/tmp/demo/
Thanks in advance!
I've now come up with an alternative solution that will give me the same rendering as I was aiming for with having a UITableView expanded inside a UIScrollView.
Solution
I removed the UIScrollView (Editor->Unembed) and set the size of the UITableView to cover whole screen (in UI editor) and I, visually in UI editor, moved the UITextFieldView in to the UITableView (as footer view).
The tiny shadow (gradient view) below the last row in the expanded table was also easy to implement within this solution since no change was needed; UITableView's delegate implemented viewForFooterInSection method, which returned a simple gradient view.