I have a UIViewController that is able to show its content via a UICollectionView (cv) or via aUITableView (tv).
Both views are added to the view hierarchy via storyboard and I'm just hiding the one that should not be shown. Both the UICollectionView and the UITableView have the same data source.
The UICollectionView is showing the content in a two items per row grid, the UITableView obviously in a one item per row grid.
So what I want to happen is if the user decides to switch between the views the newly shown view should scroll to the element that was visible in the other view.
I did some experimenting with scrollToRowAtIndexPath:atScrollPosition:animated: and scrollToItemAtIndexPath:atScrollPosition:animated: with no success.
I understand, that both the UICollectionView and UITableView are able to return their current visible items'/rows' indexPaths, but how can i transform those indexPath to a indexPath of the other view and tell them to scroll to that element?
My current touch event handling of the switch button looks like this:
- (IBAction)switchView:(id)sender {
self.showListView = !self.showListView;
UIView *fromView, *toView;
NSIndexPath *indexPath;
if (self.showListView)
{
fromView = self.ArticleCollection;
toView = self.ArticleList;
CGRect visibleRect = (CGRect){.origin = self.ArticleCollection.contentOffset, .size = self.ArticleCollection.bounds.size};
CGPoint visiblePoint = CGPointMake(100, CGRectGetMidY(visibleRect));
indexPath = [self.ArticleCollection indexPathForItemAtPoint:visiblePoint];
}
else
{
fromView = self.ArticleList;
toView = self.ArticleCollection;
CGRect visibleRect = (CGRect){.origin = self.ArticleList.contentOffset, .size = self.ArticleList.bounds.size};
CGPoint visiblePoint = CGPointMake(100, CGRectGetMidY(visibleRect));
indexPath = [self.ArticleList indexPathForRowAtPoint:visiblePoint];
}
NSInteger row = indexPath.row;
if (self.showListView) {
[self.ArticleList scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:row*2 inSection:0]
atScrollPosition:UITableViewScrollPositionTop
animated:NO];
} else {
[self.ArticleCollection scrollToItemAtIndexPath:[NSIndexPath indexPathForItem:ceil(row/2)
inSection:0
atScrollPosition:UICollectionViewScrollPositionCenteredVertically
animated:NO]];
}
// Here a transitionFromView:ToView:duration:options:completion call follows
}
Thanks in advance for any contribution to my problem.
Appart from a little typo inside the scrollToItemAtIndexPath:atScrollPosition:animated: call:
[self.ArticleCollection scrollToItemAtIndexPath:[NSIndexPath indexPathForItem:row
inSection:0]
atScrollPosition:UICollectionViewScrollPositionCenteredVertically
animated:NO];
I did think indexPath.row would hold different values whether it came from the cv's visible indexPath or the tv's visible indexPath so I calculated the new row by multiplying or dividing through 2. But as it turns out the rows are identical in both cases, so I just needed to use the same value for both cases:
NSInteger row = indexPath.row;
if (self.showListView) {
[self.ArticleList scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:row inSection:0]
atScrollPosition:UITableViewScrollPositionTop
animated:NO];
} else {
[self.ArticleCollection scrollToItemAtIndexPath:[NSIndexPath indexPathForItem:row
inSection:0]
atScrollPosition:UICollectionViewScrollPositionCenteredVertically
animated:NO];
}
Sorry for the inconvenience.
Related
I have a horizontal UICollectionView that implements UICollectionViewLayout and uses targetContentOffsetForProposedContentOffset for a custom fling/paging functionality. Each cell is nearly the width of the screen with the next cell showing slightly to the right. The functionality works great, but I want to be able to delete the cell at index 0 when the current cell is index 1. I'm currently able to calculate and do this just fine, but upon deleting the index 0 cell it slides to the next cell (old index 2, new 1 (after deleting 0)) because of the current content offset. I'm not sure how I can delete index 0 while maintaining the current layout.
Right now in my delegate I'm doing:
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView{
float cellWidth = self.collectionView.bounds.size.width - 20;
float currentPage = self.collectionView.contentOffset.x / cellWidth;
if (currentPage == 1) {
[self remove:0];
}
}
-(void)remove:(int)i {
[self.collectionView performBatchUpdates:^{
[self.data removeObjectAtIndex:0];
NSIndexPath *indexPath =[NSIndexPath indexPathForRow:0 inSection:0];
[self.collectionView deleteItemsAtIndexPaths:[NSArray arrayWithObject:indexPath]];
} completion:^(BOOL finished) {
}];
}
So the calculation and deletion works fine, but upon deleting [0], the collection view is scrolled to the next cell..and I'm not sure how to stop it.
I've tried self.collectionview.contentOffset = CGMakePoint(0,0) after the deleting, but the transition is still noticeably buggy. Any ideas on how to approach this problem?
so after a ton of trial and error I was able to resolve the issue. It might be a little hackish but I just wrapped the batch with a uiview animation that has no duration. Basically it overrides the deletion animation
[UIView animateWithDuration:0 animations:^{
[self.collectionView performBatchUpdates:^{
[scrollView setContentOffset:CGPointMake(0, 0) animated:NO];
[self.data removeObjectAtIndex:0];
NSIndexPath *indexPath =[NSIndexPath indexPathForRow:0 inSection:0];
[self.collectionView deleteItemsAtIndexPaths:#[indexPath]];
[self.collectionView.collectionViewLayout invalidateLayout];
} completion:nil];
}];
I have a UITextView in a UITableViewCell. They both dynamically size itself to fit. The problem I have is that when the texview reaches below the bottom of the screen (from making new lines), the tableView does not scroll to its location.
I tried this code, but it didn't work.
- (void)textViewDidChangeSelection:(UITextView *)textView {
[textView scrollRangeToVisible:textView.selectedRange];
}
You can find the cell containing the textView and tell the table view to scroll to that cell:
UIView *parentView = textView.superview;
while(![parentView isKindOfClass:[UITableViewCell class]]) {
parentView = parentView.superview;
}
NSIndexPath *indexPath = [self.tableView indexPathForCell:(UITableViewCell*)parentView];
[self.tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionMiddle animated:YES];
You should add code below to scroll the tableView too:
- (void)textViewDidChangeSelection:(UITextView *)textView {
[textView scrollRangeToVisible:textView.selectedRange];
// since you are only scrolling the table a little bit and not by a whole cell, then change the scrollView.offset
CGPoint offset = tableView.contentOffset;
offset.x += 20; // an example. You need to change this based on the new size of the textview.
tableView.contentOffset = offset;
}
I have implemented SKSTableView and I used this command:
[tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionTop animated:YES];
when I press a cell that is expandable, and it goes on top. Now I'd like that when I scroll the expanded cell which went to top to stay fixed there until it's closed. Can I do that?
In UITableView you have the UIScrollView methods. So you can do something like this:
-(void)scrollViewDidScroll:(UIScrollView *)scrollView {
UITableViewCell *cell =[self.myTableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]];
CGRect cellFrame = [cell convertRect:cell.frame toView:self.view];
if (self.isStickToTop) {
self.cell.frame = CGRectMake(0, 0, self.cellFrame.width, cellFrame.size.height);
}
else {
self.cell.frame = CGRectMake(0, scrollView.contentOffset.y, self.cellFrame.width, cellFrame.size.height);
}
}
The isStickToTop is your maintenance BOOLvariable that will indicate if the cell stick to top or not. Don't say u don't know ;)
I have composed a tableview with some custom cells. Some of the custom cells contain UIWebView, some has UISlider plus UITextField, some has UIButton.
Now when I click on textfield which is uitableviewcell's subview at the bottom of the UITableView, I can't manage to get my table cell properly visible above the keyboard.
Using of UITableViewController instead of UIViewController is not possible for me. Because my UI structure is somewhat weird and dynamic. In simple way it is like
UIView -> UIScrollView with paging -> x number of UITableViews -> y number of UITableViewCell -> z number of UITextFields, UISliders, UIWebViews, UIButtons, UILables, etc.
I have also tried below code to make it work.
- (void)textFieldDidBeginEditing:(UITextField *)textField
{
UITableViewCell *cell = (UITableViewCell*) [[textField superview] superview];
[table_Question[pageNumber] scrollToRowAtIndexPath:[table_Question[pageNumber] indexPathForCell:cell] atScrollPosition:UITableViewScrollPositionTop animated:YES];
}
but it is not working :(
Thanks,
EDIT 1:
I have checked references for textfield, tableviewcell and tableview. So reference of object is not the problem.
Try this code....
- (void)textFieldDidBeginEditing:(UITextField *)textField
{
UITableViewCell *cell = (UITableViewCell*) [[textField superview] superview];
NSIndexPath *indexPath = [[table_Question[pageNumber] indexPathForCell:cell];
int totalRows = 0;
if([[table_Question[pageNumber] numberOfSections] > 1)
{
for(int i = 0; i<([indexPath.section] - 1);i++)
{
totalRows += [[table_Question[pageNumber] numberOfRowsInSection:i];
}
}
totalRows += indexPath.row;
int yPosition = totalRows * cell.frame.size.height;
CGPoint offset = CGPointMake(0, yPosition);
[[table_Question[pageNumber] setContentOffset:offset animated:YES];
}
This will helps for you...
Try this code
- (void)textFieldDidBeginEditing:(UITextField *)textField
{
UITableViewCell *aCell = [table_Question[pageNumber] cellForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:1]];
CGSize cellSize = aCell.frame.size;
[table_Question[pageNumber] setContentOffset:CGPointMake(0, cellSize.height*2) animated:YES];
}
As per my understanding, you want to show the UITextField as well as the keyboard, when the user starts editing the UITextField inside the UITableViewCell.
If that's the case, you can set the contentInset of the UITableView to the required size and then just scroll to the required index path.
Following is a sample code:
-(BOOL)textFieldShouldBeginEditing:(UITextField *)textField
{
#try
{
table_Question[pageNumber].contentInset = UIEdgeInsetsMake(CGFloat Req_top, CGFloat Req_left, CGFloat Req_bottom, CGFloat Req_right);
[table_Question[pageNumber] scrollToRowAtIndexPath:[table_Question[pageNumber] indexPathForCell:cell] atScrollPosition:UITableViewScrollPositionTop animated:YES];
//Add this to you code to scroll in the tableview gets hidden by keyboard
CGPoint scrollPoint = CGPointMake(0, Required_Height);
[yourScrollView setContentOffset:scrollPoint animated:YES];
}
}
I have a UITableview in my xcode project.In that Comments are listed.How can i focus on a last cell of my TableView with scrolling animation?
Below method will find the last index of you table view and will focus to that cell with an animation
-(void)goToBottom
{
NSIndexPath *lastIndexPath = [self lastIndexPath];
[<YOUR TABLE VIEW> scrollToRowAtIndexPath:lastIndexPath atScrollPosition:UITableViewScrollPositionBottom animated:YES];
}
Code to find the last index of your tableview.
-(NSIndexPath *)lastIndexPath
{
NSInteger lastSectionIndex = MAX(0, [<YOUR TABLE VIEW> numberOfSections] - 1);
NSInteger lastRowIndex = MAX(0, [<YOUR TABLE VIEW> numberOfRowsInSection:lastSectionIndex] - 1);
return [NSIndexPath indexPathForRow:lastRowIndex inSection:lastSectionIndex];
}
Add this code somewhere in your view controller
[self performSelector:#selector(goToBottom) withObject:nil afterDelay:1.0];
Keep in mind that a UITableView inherits from UIScrollView
-(void)scrollToBottom:(id)sender
{
CGSize r = self.tableView.contentSize;
[self.tableView scrollRectToVisible:CGRectMake(0, r.height-10, r.width, 10) animated:YES];
}
execute it like
UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
button.frame = CGRectMake(…);
[button addTarget:self action:#selector(scrollToBottom:) forControlEvents:UIControlEventTouchUpInside];
(You dont need to use performSelector:…)
Another solution would be selecting the last row. The table view will take care of the rest.
-(void)scrollToBottom:(id)sender
{
NSInteger lasSection = [self.tableView numberOfSections] - 1;
NSInteger lastRow = [self.tableView numberOfRowsInSection:lasSection]-1;
//this while loops searches for the last section that has more than 0 rows.
// maybe you dont need this check
while (lastRow < 0 && lasSection > 0) {
--lasSection;
lastRow = [self.tableView numberOfRowsInSection:lasSection]-1;
}
//if there is no section with any row. if your data source is sane,
// this is not needed.
if (lasSection < 0 && lastRow < 0)
return;
NSIndexPath *lastRowIndexPath =[NSIndexPath indexPathForRow:lastRow inSection:lasSection];
[self.tableView selectRowAtIndexPath:lastRowIndexPath animated:YES scrollPosition:UITableViewScrollPositionBottom];
}
the button is the same.
Swift 3:
let indexPath = IndexPath(row: /*ROW TO SCROLL TO*/, section: /*SECTION TO SCROLL TO*/)
tableView.scrollToRow(at: indexPath, at: .bottom, animated: true)