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. :)
I have a UITableView where I add rows dynamically. Basically a chat app.
I need to scroll the UITableView to bottom after reloadData
My code is:
It scrolls to here:
https://dl-web.dropbox.com/get/Screen%20Shot%202014-12-05%20at%2013.04.23.png?_subject_uid=44249794&w=AADgJkJJOGK_ANH_cXWnxeXmhFmF5KHjZllg-kP66HARdw
When manually scrolling, I can scroll even further to here: https://dl-web.dropbox.com/get/Screen%20Shot%202014-12-05%20at%2013.05.19.png?_subject_uid=44249794&w=AADtQEfMHMy0Ounri5W3gBTLNgR4uckT_gBdYQel9vD1qQ
My code:
[_chatTable reloadData];
NSIndexPath* indexPath = [NSIndexPath indexPathForRow: [[AppData getChatLog]count]-1 inSection: 0];
[_chatTable scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionTop animated:NO];
I need the UITableView to scroll all the way to the bottom.
Sorry,I couldn't open up the given links.Try this
UITableView *yourTableView = yourTableViewToBeSetHere;
CGRect lastCellRect = CGRectMake(0, CGFLOAT_MAX, 50, 50);
// Scroll to the particular rect
[yourTableView scrollRectToVisible:lastCellRect
animated:YES];
Good luck!
I have a tableView with dynamically generated custom cells and one of the custom cell contains a scrollView that can scroll vertically and a page control is attached to it.
The user interface structure of the tableview can be seen in the photo.
I am refreshing the position of the page control in the scrollViewDidScroll method as follows.
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
NSIndexPath *indexPathPageControl=[NSIndexPath indexPathForRow:0 inSection:0];
UITableViewCell * cell = (UITableViewCell*)[self.sonDakikaTableView cellForRowAtIndexPath:indexPathPageControl];
CGFloat xOffset = scrollView.contentOffset.x;
CGFloat frameWidth = scrollView.frame.size.width;
int page = floor((xOffset - frameWidth / 2) / frameWidth) + 1;
UIPageControl *pageControl = (UIPageControl *)[cell.contentView viewWithTag:14];
pageControl.currentPage = page; // assigning to pagecontroll
}
It is working very fine when the user is scrolling vertical in the scrollView.
However when the user scrolls down on the tableView and the cell with vertical scrollView is not visible anymore, the integer page variable is being updated with 0 and when the user scrolls up and table view cell is visible again, the location of page control is being reset.
I will appreciate if someone can give me an idea about how to prevent this.
Thanks in advance.
i added this code
(void)scrollViewDidScroll:(UIScrollView *)_scrollView
{
if (_scrollView == self.tableView) return;
and in your case
(void)scrollViewDidScroll:(UIScrollView *)_scrollView
{
if (_scrollView == self.sonDakikaTableView) return;
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?
I have a UITableview with 2 sections, 1 row in the 1st section, and a variable amount of rows for the second section. When the user scrolls the table up (finger moving upwards), then the tableview does it's default thing, but when the user is at the top of the uitableview and scrolls down (finger moving downwards) then it should look like the first cell in the first section height increases as much as the user scrolls down (and releasing the touch will change the height of the row back to it's original height of 100). I tried
-(void)scrollViewDidScroll:(UIScrollView*)scrollView{
if(scrollView.contentOffset.y < 0){
[mainTable reloadRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:0 inSection: 0]] withRowAnimation:UITableViewRowAnimationNone];
}
}
-(CGFloat)tableView:(UITableView*)tableView heightForRowAtIndexPath:(NSIndexPath*)indexPath{
if(indexPath.section == 0 && tableView.contentOffset.y < 0){
return -tableView.contentOffset.y+100;//Default height is 100
}
}
but the reloadRows method resets the contentOffset to 0 so the tableview just stutters when i pull down. It seems like if this doesn't work, then i'll have to write everything on a UIScrollView and it seems like a huge pain without recyclable cells.
Does anybody have any suggestions?
No need to reload the row, i just changed the frame directly. Make sure you set the cell.layer.masksToBounds = YES and make the subViews in the cell to be larger than the original cell height.
-(void)scrollViewDidScroll:(UIScrollView *)scrollView{
if (mainTable.contentOffset.y < 0) {
UITableViewCell *cell = [mainTable cellForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]];
float rawr = mainTable.contentOffset.y;
cell.frame = CGRectMake(cell.frame.origin.x, rawr, cell.frame.size.width, 100-rawr);
}
}