I want to implement an UITableView with some animation while the user is scrolling. Only the first visible (top most) cell has to be an different layout. Before the second cell reaches the top of UITableView I want to start the small animation. (changing background alpha and width of cells contentView etc.)
How could I handle this?
[UPDATE]
What I have tried so far:
using UIScrollView delegate method scrollViewDidScroll:(UIScrollView *)scrollView
together with UITableView´s visibleCells property using the first item of it to get the
reference with no luck
This is what I have done:
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
if (!decelerate) {
[self scrollingFinish];
}
}
..
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
[self scrollingFinish];
}
..
- (void)scrollingFinish {
NSIndexPath *firstVisibleIndexPath = [[self.tableView indexPathsForVisibleRows] objectAtIndex:0];
NSLog(#"first visible cell's section: %i, row: %i", (int) firstVisibleIndexPath.section, (int) firstVisibleIndexPath.row);
APFCategoryCell * cell = (APFCategoryCell*) [self.tableView cellForRowAtIndexPath:firstVisibleIndexPath];
[cell animateCell];
[self.tableView scrollToRowAtIndexPath:firstVisibleIndexPath atScrollPosition:UITableViewScrollPositionTop animated:YES];
}
The problem appears, if the user is scrolling down because the animated cell is still visible and the next one gets animated. So two or more cells are animated. I could going on and implement an method in my custom cell like - (void) resetAnimatedCell but maybe there is an much more elegant way for doing this?
Related
I am using the following code to find the visible cells in my tableview. Everytime the user scrolls only one UITableViewCell is visible to the user but the code below returns two visible rows because this is how the UITableView functioning when user scrolls to a cell.
NSArray *visibleCells = [self.tableView indexPathsForVisibleRows];
for (NSIndexPath *cellIndexPath in visibleCells)
{
UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:cellIndexPath];
if ([cell isKindOfClass:[C8SubmittedContentTableViewCell class]]) {
[submittedContentTableViewCell play];
}
}
I run the code above at
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
But as i mentioned UITableView returns two visible cells. How would i know which one of them is really visible to the user? Because if i have two or three videos in a row i need to start playing only the one that is really visible to the user and at the same time stop playing the previous one.
Any thoughts or help appreciated!
Here is possible way:
CGRect cellRect = [self.tableView rectForRowAtIndexPath:cellIndexPath];
CGRect visibleRect;
visibleRect.origin = self.tableView.contentOffset;
visibleRect.size = self.tableView.bounds.size;
BOOL isCellVisible = CGRectContainsRect(visibleRect, cellRect);
Your layout is not clear so possible CGRectIntersectsRect might be needed, or even manual rect to rect matching (say, if cell rect is always bigger then table clip area, the percentage of overlapping should be used).
Use delegate's willDisplayCell for starting video playing and didEndDisplayingCell for stopping.
I have two UICollectionViews and when the user scrolls on one, the other should also change by scrolling to the same index path. I've implemented the following code so that, when the first UICollectionView decelerates and lands on one cell, the second UICollectionView should automatically scroll to that same IndexPath:
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
// Only do this for the first UICollectionView
if (scrollView.tag == 0) {
// Find the new IndexPath
CGRect visibleRect = (CGRect){.origin = self.filterCollectionView.contentOffset, .size = self.filterCollectionView.bounds.size};
CGPoint visiblePoint = CGPointMake(CGRectGetMidX(visibleRect), CGRectGetMidY(visibleRect));
NSIndexPath *visibleIndexPath = [self.filterCollectionView indexPathForItemAtPoint:visiblePoint];
// On main thread, scroll second CollectionView to visibleIndexPath
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(#"Row: %li, Sec: %li", visibleIndexPath.row, visibleIndexPath.section);
[self.filterTitleCollectionView reloadData];
[self.filterTitleCollectionView layoutIfNeeded];
[self.filterTitleCollectionView setNeedsDisplay];
//scroll titles to same index path
[self.filterTitleCollectionView scrollToItemAtIndexPath:visibleIndexPath atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:YES];
NSLog(#"Row: %li, Sec: %li", self.filterTitleCollectionView.indexPathsForSelectedItems[0].row, self.filterTitleCollectionView.indexPathsForSelectedItems[0].section);
});
}
}
For some reason, the filterTitleCollectionView does not scroll to the visibleIndexPath. Or if it does scroll, it scrolls to the wrong cell (in a predictable pattern). You can see the video of it here:
https://streamable.com/q2lnnw
The row number is in the title of the cell. As you can see, the second UICollectionView keeps scrolling to row 2 for some reason.
The correct IndexPath is printed, but it scrolls to the wrong IndexPath or doesn't scroll at all.
Using the willDisplayCell method I got the index of the last visible cell:
- (void)collectionView:(UICollectionView *)collectionView willDisplayCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath {
indexVisibleCell = [indexPath indexAtPosition:[indexPath length] - 1];
}
where indexVisibleCell is an NSUInteger that is declared in the class' interface. My problem is that I want to shift only one cell when user taps on the right or left button. I have applied this code for that purpose:
- (IBAction)rightButton:(id)sender {
NSUInteger indexOfScrolledCell;
indexOfScrolledCell = indexVisibleCell;
if (indexVisibleCell < 9) {
NSIndexPath *path = [NSIndexPath indexPathForRow:indexOfScrolledCell+1 inSection:0];
[self.obj_CollectionView1 scrollToItemAtIndexPath:path atScrollPosition:UICollectionViewScrollPositionLeft animated:YES];
}
}
as I show 3 cells at a time. By using that rightButton action method it shifts 3 cells at a time, not one. Please help me figure out how to scroll one cell at time.
Try using UICollectionView with paging enabled. Use following code, hopefully it works out for you.
[yourCollectionView setPagingEnabled:YES];
Use visibleCells to get your visible Cells, get the leftmost Cell index to figure out the next index, then scroll to that Cell on the left (similar logic to your code).
willDisplayCell is not reliable since it is called both times when you scroll left/right, and you're thinking that index is the left/right most is not correct.
Edit:
Or you need to compare the index in willDisplayCell correctly, to get the correct left/right most index, based on your logic.
For right button you can use following
[self.obj_CollectionView1 scrollToItemAtIndexPath:path atScrollPosition:UICollectionViewScrollPositionRight animated:YES];
and for left button following
[self.obj_CollectionView1 scrollToItemAtIndexPath:path atScrollPosition:UICollectionViewScrollPositionLeft animated:YES];
I am changing UICollectionViewScrollPosition so that when you scroll to right new item shows up on right side of collection view and when you scroll to left new item shows on left side of collection view.
I am trying to implement is "snap to cell" effect in my UITableView.
My UITableView has 3 equally sized cells, but for some reason the UITableView always snaps to cell 0 or cell 1 and doesn't ever snap to the third cell. its also wrong by about 50 points too low, no matter how hard you slide. I use the most obvious code to create the effect so I can't understand why it doesn't work.
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset {
NSIndexPath *pathForTargetTopCell = [self.tableView indexPathForRowAtPoint:CGPointMake(CGRectGetMidX(self.tableView.bounds), targetContentOffset->y)];
DLog(#"indexPathForRow %d", pathForTargetTopCell.row);
targetContentOffset->y = [self.tableView rectForRowAtIndexPath:pathForTargetTopCell].origin.y;
}
Any thoughts about what could be going wrong?
EDIT:
I suspect this has something to do with the status bar and nav bar on top, since its off by exactly 64 points. Still doesn't explain why it doesn't recognise the last cell though..
For the status bar offset, do this in your VC's viewDidLoad:
if([self respondsToSelector:#selector(setAutomaticallyAdjustsScrollViewInsets:)])
[self setAutomaticallyAdjustsScrollViewInsets:NO];
edit
Just for testing, could you add 4 equally size cells and try snapping to the 3rd and 4th cells?
Modify your scrollViewWillEndDragging implementation as follows:
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset {
NSIndexPath *pathForCenterCell = [self.tableView indexPathForRowAtPoint:CGPointMake(CGRectGetMidX(self.tableView.bounds), CGRectGetMidY(self.tableView.bounds))];
[self.tableView scrollToRowAtIndexPath:pathForCenterCell atScrollPosition:UITableViewScrollPositionMiddle animated:YES];
}
I am implementing a UICollectionView in my iOS app. I have it so each cell is the width of the screen and I want to have it so when scrolling horizontally it locks onto the visible cell and moves it to the center of the screen. The code bellow I have only works for the first cell. I don't understand how to get this to work for any cell the user has visible.
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView{
int index = 0;
for (TextCell * t in [_tabBarCollectionView visibleCells]) {
if ((t.center.x>0)&&(t.center.x<[[UIScreen mainScreen]bounds].size.width)) {
[_tabBarCollectionView scrollToItemAtIndexPath:[NSIndexPath indexPathForRow:index inSection:0] atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:YES];
break;
}
index++;
}
}
You can turn paging on for the collection view and it will have that effect. Go to the xib file or storyboard that has the collection added and enable paging under its properties.