Find the visible UITableViewCell when user scrolls to it - ios

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.

Related

Get `UITableViewCell` indexPath using `UIScrollView`

I have a UITableViewCell which contains a UICollectionView. The UITableViewCell also contains a UIPageControl. I want to change the dots as the UICollectionView is swiped. I am using
-(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
to get the current visible UICollectionViewCell. But my problem is that since the UICollectionView lies in UITableViewCell to fetch the reference to the collectionView I require the indexPAth of the current table view in which collection cell is being swiped. How can I do this?
I want to do this:
-(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
// Change the dots
CustomTableCell *tableCell = [self.currentTable cellForRowAtIndexPath:_tablecellIndex];
NSInteger currentIndex = tableCell.currentCollectionView.contentOffset.x / tableCell.currentCollectionView.frame.size.width;
tableCell.currentPageControl.currentPage = currentIndex;
}
But how do I get the _tablecellIndex?
I tried doing :
NSArray *indexes = [self.currentTable visibleCells];
_tablecellIndex = indexes[0];
But this is not always true as sometimes the table cells are displayed half and user is swiping second cell.
You need to ask the tableview itself, what indexpath a given cell has. You do that with this command :
[self.formTableView indexPathForCell:myCell];
The other problem in your case is that you are within the collection view on the cell, and not within the tableview itself. So theres a few ways to do that - one nice way is to set up a delegate on the cell that can access the tableview. Thats a bit involved, so heres an even simpler way (self in this case is the cell object):
UITableView *parentTableView = (UITableView*)self.superview;
NSIndexPath *cellIndexPath = [parentTableView indexPathForCell:self];
That should do the job.

Track coordinates of UITableViewCell subview

I would like to fire an event when the subview of a UITableviewCell reaches a certain point on the screen, say for example when its origin.y reaches 44 points. It would also be nice to know if it was being scrolled up or down when it reached that point. I was playing with KVO on the frame of the subview but this seems fixed to the cell so no changes with that. Is this task possible?
Vertical position of UITableViewCell is defined by its frame property, which represents position and size of that cell within its superview, UITableView. Typically, the frame property of the cell is changing only once for every time that UITableView requests a cell from its delegate for specific index path. That's it, UITableView gets a cell, places it in itself and that cell just lays there unchanged until rectangle stored in bounds property of UITableView ceases to include rectangle stored in the frame property of that cell. In that case UITableView marks that cell as hidden and places it into the pool of reusable cells.
Since the process of scrolling in essence is not a repositioning of subviews – it is merely a curious illusion of shifting a bounds viewport of UITableView – constant observing of UITableViewCell's properties are pointless.
Moreover, the frame property of subview of UITableViewCell also represents a position and size of that subview within its container, UITableViewCell. It is also will not change on scroll.
You need to observe changes in UITableView bounds property, which is also represented by contentOffset by the way. UITableView happens to be a subclass of UIScrollView, so you can use its delegate methods, such as -scrollViewDidScroll:, like in this simple example:
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
UITableView *tableView = (UITableView *)scrollView;
// current position
CGFloat currentY = tableView.bounds.origin.y;
// current inset
CGFloat currentInset = tableView.contentInset.top;
// trigger line position
CGFloat triggerY = currentInset + currentY + kYourTriggerPosition;
// nice visual mark
UIView *line = [tableView viewWithTag:88];
if (!line) {
line = [[UIView alloc] initWithFrame:CGRectZero];
line.tag = 88;
line.backgroundColor = [UIColor redColor];
[tableView addSubview:line];
}
line.frame = CGRectMake(0, triggerY, tableView.bounds.size.width, 1);
// determine scroll direction
BOOL scrollingUp = currentY > self.previousY;
// all visible cells
NSArray *visibleCells = tableView.visibleCells;
[visibleCells enumerateObjectsUsingBlock:^(UITableViewCell *cell, NSUInteger idx, BOOL *stop) {
// subview
UIView *subview = [cell viewWithTag:kYourSubviewTag];
// subview frame rect in UITableView bounds
CGRect subviewRect = [subview convertRect:subview.frame toView:tableView];
// trigger line within subview?
BOOL triggered = (CGRectGetMinY(subviewRect) <= triggerY) && (CGRectGetMaxY(subviewRect) >= triggerY);
if (triggered) {
NSIndexPath *indexPath = [tableView indexPathForCell:cell];
NSLog(#"moving %#, triggered for cell at [%2d:%2d]", #[#"down", #"up"][scrollingUp], indexPath.section, indexPath.row);
[tableView selectRowAtIndexPath:indexPath animated:NO scrollPosition:UITableViewScrollPositionNone];
}
}];
// save current position for future use
self.previousY = currentY;
}
Reach that subview of UITableViewCell with cellForRowAtIndexPath or tableView.visibleCells, then call convertRectToView: on that subview.
convertRectToView: allows you to do translations on different coordinate systems. For example, you can detect where that subview appears on screen by translating its frame within its superview into viewController.view
For more: Apple Documentation
Since I can not comment I am writing as an Answer
Changing the answer for the requirement.
Here is how I think it can be done, you need to have your custom UITableViewCell which has a function which can take in co-ordinates (again based on your logic if you just want an intersection where a cell just touches a boundary or if it has to be at a precise position in a frame), so your function would take the co-ordinates and will return a true and a false if it will tell you if the condition is met, and in your cellForTable function you call the function of UITableView cell to check if your condition is met, if it is in your view you create a subview at the exact location. You can also modify the function to return you the exact frame-cordinates so you can use them to create a subview\
Here's a simple approach, which you can use if you have only one section without section header.
Add this to your implementation:
CGFloat lastContentOffSet;
And then add this delegate method of scrollview as tableview is also a scrollview.
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
CGFloat cellHeight = 50;
CGFloat touchingPoint = 44.0f;
NSInteger rowNo = floor(scrollView.contentOffset.y / cellHeight);
NSInteger startPoint = (rowNo * cellHeight);
if (scrollView.contentOffset.y > lastContentOffSet) {
NSLog(#"Row %ld scrolled down", (long)rowNo);
if (scrollView.contentOffset.y > startPoint + touchingPoint) {
// Do something here
NSLog(#"Do something here");
}
}
else {
NSLog(#"Row %ld scrolled up", (long)rowNo);
if (scrollView.contentOffset.y > startPoint + touchingPoint) {
// Do something here
NSLog(#"Do something here");
}
}
lastContentOffSet = scrollView.contentOffset.y;
}
Change value of the cellheight according to your tableview cell and the distance of that subview with the cell.
Let me know if this code helped. :)

Parallax Scrolling Effect like TodoMovies 3

I was wondering how I might go about implementing a scrolling parallax effect similar to what is seen in TodoMovies 3?
In TodoMovies 3, the background of (what I think is) a UITableViewCell moves faster than the scrolling of the page, making a really awesome effect.
How would you go about detecting the scroll of the TableView's scrollview and also adjust the background image of the cell in a performant way?
Or is the effect impossible to achieve with UITableView?
I was able to achieve the effect by placing this code in my UITableViewController
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
for (TVTableViewCell *cell in [self.tableView visibleCells]) {
[cell adjust:(cell.frame.origin.y - scrollView.contentOffset.y)];
}
}
And this code in my UITableViewCell
- (void)adjust:(CGFloat)offset {
CGRect frame = self.image.frame;
frame.origin.y = (offset / 10.0);
self.image.frame = frame;
}
Modified from oleb.net/blog/2014/05/parallax-scrolling-collectionview. Thanks mustafabesnili for the link.

How do I change the height of a table cell that has already loaded?

Currently I have webviews loading in customized uitableview cells. The problem is the web views have variable sizes. In webViewDidFinishLoad I am able to set the size of the web view based on the actual size of the html document just fine. My problem is the table cells which have already had their height set in heightForRowAtIndexPath before the web views having finished loading. How can I change the height of a table cell after it has already been loaded?
Ideally I feel like I should be able to use some line of code like this.
cellW.frame = cellW.cellWebView.frame;
However I don't seem to have access to cell information in heightForRowAtIndexPath. I've felt like I've explained the situation fairly well, but any code you think I should post I can put up here. I've tried a lot things (so there comments and failed attempts at this everywhere), but the main issue is I can't seem to access cell information in the right places such as heightForRowAtIndexPath. If I could even set cell information somehow in webViewDidFinishLoad, I could simply set the frame of the cell where I am also setting the frame size of the web view.
Below is the setup for my table cell subclass.
#import <UIKit/UIKit.h>
#import "DefinitionsAndConstants.h"
#interface UITableViewCellWebView : UITableViewCell
{
UIWebView *cellWebView;
}
#property (nonatomic,retain) UIWebView *cellWebView;
#end
Here is what I have tried last trying to use part Gavin's code. But of course there is no way to set the table cell now that I've gotten out because cellForRowAtIndexPath is not assignable.
-(void)webViewDidFinishLoad:(UIWebView *)webViews {
[webViews stringByEvaluatingJavaScriptFromString:#"twitterfy()"];
[webViews stringByEvaluatingJavaScriptFromString:#"document.getElementById('tweettext').innerHTML=tweet"];
NSString* h = [webViews stringByEvaluatingJavaScriptFromString:#"getDocHeightMax()"];
int height = [h intValue];
webViews.frame = CGRectMake(0.0f, 0.0f, 320.0f, height);
NSString* i = [webViews stringByEvaluatingJavaScriptFromString:#"return indexPath"];
int ii = [i intValue];
NSIndexPath* ip = [NSIndexPath indexPathForRow:ii inSection:0];
UITableViewCellWebView *tableCell = (UITableViewCellWebView *)[self.tableView cellForRowAtIndexPath:ip];
if (tableCell) {
tableCell.frame = webViews.frame;
}
NSLog(#"Got height from webview %d", height);
}
I would set up a mutable array to read heights from. In viewDidLoad, you'll have to assign some starting values (probably just a bunch of #"44.0", it doesn't really matter, it won't be used for much yet). In your heightForRowAtIndexPath:, just return the number from that array. Then in webViewDidFinishLoad, replace the heights in the array with the height you actually need for that cell (the NSString *h in the code you posted, I believe), and call reloadData on your table view. reloadData will hit heightForRowAtIndexPath:, which will look at the heights array, which now has the actual height needed for that cell, so everything should be shiny.
You should be able to do something like this for your heightForRowAtIndexPath method:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
MyTableViewCell *tableCell = (MyTableViewCell *)[self.tableView cellForRowAtIndexPath:indexPath];
if (tableCell) {
return tableCell.cellWebView.frame.size.height;
}
return 44;
}
This way, if the table cell is visible, it'll set the height according to the height of the embedded web view, otherwise it'll use a default height. In order to make the table view refresh the height of the cell, you'll have to call the following once the web view is sized properly:
[self.tableView beginUpdates];
[self.tableView endUpdates];
That won't make it reload the cells, which could screw things up for you, it'll just make it check the heights again, and the cell height will animate to the new height.

How to track movement of a cell in UICollectionView after screen rotation

I have an UICollectionView. When I touch one of the cells I present a popover view from its location with the arrow pointing to the cell with some extra information about the cell.
When I rotate the device, the UICollectionView automatically repositions its cells.
What I'd like to do, is to reposition my popover view automatically so it points to the same cell as before (which is now at a different location in the UICollectionView)
What I'm having trouble is tracking (finding out) what is my cell's new location, so I can manually represent the popover view from the cell's new location.
I tried attaching and storing an "idString" for the cell for comparison, but this for some reason returns the cell's old frame from before the screen rotation
NSArray* visibleCells = [UIAppDelegate.ocollectionView visibleCells];
for (UICollectionViewCell *cell in visibleCells) {
if ([cell.idString isEqualToString:self.idString] ) {
NSLog (#"we have a match!!! %#", cell);
CGRect rectInCollectionView = cell.frame;
rect = [UIAppDelegate.collectionView convertRect:rectInCollectionView toView:[UIAppDelegate.collectionView superview]];
}
}
any ideas appreciated. thank you.
Where is this code being placed? I guess it should be placed on a delegate method like this kind:
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation;
So the UICollectionView has redrawn its new cell's position.

Resources