Autolayout and Collection View Issue - ios

So the basic issue is that when I click the cell, it should go to cell index 1 in the new view controller, but when you have autolayout on. The collectionview content offset change goes away and it's reset. Turn it off, works fine. Autolayout is somehow causing content offset to reset but I'm not sure why or how to resolve this.
Code available here.
https://github.com/HaloZero/AutolayoutCollectionViewIssue

use you code as :--
- (void) viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[self performSelectorOnMainThread:#selector(doyouwork) withObject:nil waitUntilDone:NO];
}
-(void)doyouwork
{
[self snapToCellAtIndex:1 withAnimation:NO];
}
- (void) snapToCellAtIndex:(NSInteger)index withAnimation:(BOOL) animated
{
NSIndexPath *path = [NSIndexPath indexPathForRow:index inSection:0];
[self.collectionView scrollToItemAtIndexPath:path atScrollPosition:UICollectionViewScrollPositionLeft animated:animated];
}
working for my side.

It is not that auto-layout is resetting the content offset, but the subviews have not been positioned when you try to snap the cell.
With auto-layout, the subviews are positioned a bit later (in which the viewWillAppear is called first). Try to use viewDidLayoutSubviews instead for your snapping of the cell:
From the documentation for viewDidLayoutSubviews:
Notifies the view controller that its view just laid out its subviews.
So after all the constraints have been calculated and your subviews have been laid, you can call the snapping method.
- (void)viewDidLayoutSubviews
{
[super viewWillLayoutSubviews];
[self snapToCellAtIndex:1 withAnimation:NO];
}

Related

ios - UITableView Freezing on load

I am building a chat application in which I have to to scroll tableview to bottom in order to show the most recent message.
What I am doing is, I am scrolling it to visible rect using the following code:
- (void)tableViewScrollToBottomAnimated:(BOOL)animated {
// return;
if (!_tableViewShouldScroll) {
return;
}
NSInteger numberOfSections = [self.tableView numberOfSections];
if (numberOfSections > 0) {
NSInteger numberOfRows = [self.tableView numberOfRowsInSection:numberOfSections - 1];
if (numberOfRows > 0) {
CGRect footer = [self.tableView rectForFooterInSection:numberOfSections - 1];
[self.tableView scrollRectToVisible:footer animated:animated];
}
}
_tableViewShouldScroll = !_tableViewShouldScroll;
}
It scrolls down pretty well but freezes for a second while going through the last cell when the view is loaded for the first time. On successive calls, this method is working fine.
PS. I am calling the scrolling method in viewdidappear.
Any help here will be appreciated.
I was calling the tableViewScrollToBottomAnimate in viewDidAppear, which was causing the freeze for a fraction of second. I called the method within viewWillLayoutSubviews and it worked like a charm.
- (void)viewWillLayoutSubviews {
[super viewWillLayoutSubviews];
[self tableViewScrollToBottomAnimated:NO];
}

tableView reloadData and deselectRowAtIndexPath

I am using the following code to deselect a selected table view cell when returning back to the table view in -viewWillAppear:animated.
[self.tableView deselectRowAtIndexPath:self.tableView.indexPathForSelectedRow animated:YES];
I also need to reload the table view's data in this case but when you do that it clears the selected state of the selected cell so you don't see any fade animation.
Is there a way to reload the table data and also preserve the selected state to create the deselect animation?
After several attempts, I've found something that works. You need to set the deselection to occur after a "delay" (of 0 seconds) in order to make sure it happens on the next draw cycle and gets animated properly.
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
NSIndexPath *indexPath = self.tableView.indexPathForSelectedRow;
[self.tableView reloadData];
[self.tableView selectRowAtIndexPath:indexPath animated:NO scrollPosition:UITableViewScrollPositionNone];
[self performSelector:#selector(deselectRow) withObject:nil afterDelay:0];
}
- (void)deselectRow
{
[self.tableView deselectRowAtIndexPath:self.tableView.indexPathForSelectedRow animated:YES];
}
Try this in your viewDidLoad:
[self setClearsSelectionOnViewWillAppear:NO];
The obvious solution suggested by #user2970476 seems to work fine on iOS 7. For iOS 8 I slightly modified #Stonz2's answer to use blocks
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
dispatch_async(dispatch_get_main_queue(), ^(void) { // necessary for iOS 8
[self.tableView deselectRowAtIndexPath:self.tableView.indexPathForSelectedRow animated:YES];
});
}
I also had to set self.clearsSelectionOnViewWillAppear = NO; in viewDidLoad because the IB setting got ignored.
You will need to reload your table view first, select the row you want to indicate then perform the deselect animation. The issue you are having is your order of operation is incorrect.
You can save the current selection with
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
[self.tableView reloadData];
[self.tableView selectRowAtIndexPath:selectedRow animated:NO scrollPosition:UITableViewScrollPositionNone];

Reloading Table and Clearing Cell Row Selection

Up to iOS 8 and Xcode 6, I was able to use a UITableViewController much like Apple's Settings to configure my applications. In segue'ing from one table back to the calling table, I would reload the calling table to update data in the cells, re-select the calling row and then let the view appear animated to clear the selection.
Here's the code in the viewWillAppear method I've been using:
[self.tableView reloadData];
[self.tableView selectRowAtIndexPath:lastselected animated:YES scrollPosition:UITableViewScrollPositionNone];
[super viewWillAppear:animated];
While the cell data is updated, the nice slow disappearance of the selected row/cell doesn't happen. It is effectively cleared immediately. I've tried just about everything I can think of and can't get this working. Please help...
What has changed and how can I get that nice transition back?
Thank you.
viewWillAppear is called before your view is on the screen. So you'll not get any animations working correctly if fired from inside that method. Use viewDidAppear instead and you should see more reliable animations on transition.. Potentially use performSelector with a delay as well if you feel it's happening too quickly.
Personally as well, I'd call [super viewDidAppear:animated]; first in this case. I can't see a reason why it'd be below the other lines of code.
Also just so I'm being 100% clear... viewWillAppear is obviously ok for reloading the tableView so lets do this...
-(void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self.tableView reloadData];
}
-(void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[self.tableView selectRowAtIndexPath:lastSelected animated:YES scrollPosition:UITableViewScrollPositionNone];
}
Or with the perform selector delay on viewDidAppear...
-(void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[self performSelector:#selector(animateCellSelectionAtIndexPath:) withObject:lastSelected afterDelay:0.2f];
}
-(void)animateCellSelectionAtIndexPath:(NSIndexPath *)indexPath
{
[self.tableView selectRowAtIndexPath:indexPath animated:YES scrollPosition:UITableViewScrollPositionNone];
}

UITableView scrollViewDidScroll fired on animation

So most of the problems people have with this particular event is to have scrollViewDidScroll fire when there is animation happening. My case is just the opposite. I feel that scrollViewDidScroll should NOT be firing in my case.
Let me further explain.
I am animating things in scrollViewDidScroll and this was working perfectly until I moved UITableView into a UIView class.
- (void) scrollViewDidScroll:(UIScrollView *)scrollView {
// Animation code here.
NSLog(#"scrollViewDidScroll");
}
- (void) scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
NSLog(#"scrollViewDidEndDecelerating");
NSArray *indexPaths = [_myTableView indexPathsForVisibleRows];
[_myTableView scrollToRowAtIndexPath:[indexPaths objectAtIndex:0] atScrollPosition:UITableViewScrollPositionTop animated:YES];
}
What this provides is a smooth scrolling experience that snaps back to a previous table row. The console can verify that scrollViewDidScroll event is firing because of scrollToRowAtIndexPath.
Console:
2014-03-31 22:21:43.346 Project[45843:a0b] scrollViewDidScroll
2014-03-31 22:21:43.379 Project[45843:a0b] scrollViewDidScroll
2014-03-31 22:21:43.429 Project[45843:a0b] scrollViewDidScroll
2014-03-31 22:21:43.462 Project[45843:a0b] scrollViewDidScroll
2014-03-31 22:21:43.479 Project[45843:a0b] scrollViewDidEndDecelerating
2014-03-31 22:21:43.496 Project[45843:a0b] scrollViewDidScroll
2014-03-31 22:21:43.513 Project[45843:a0b] scrollViewDidScroll
2014-03-31 22:21:43.529 Project[45843:a0b] scrollViewDidScroll
Onto the questions:
1. How can I ensure that the event scrollViewDidScroll only fires due to user interaction and not automation from code?
2. Is there another method that provides the same functionality as scrollToRowAtIndexPath without triggering scrollViewDidScroll?
I know this is a very old question. I just have been in a similar situation that I need to run some code only when the user is scrolling but not when I call scrollToRowAtIndexPath. I have found an easier solution for this using the scrollView variables dragging and decelerating.
we have 3 cases of scrolling here.
user is dragging
user just ended dragging and the tableview is decelrating
programmatically called scrollForRowAtIndexPath:
All of the cases above will trigger scrollViewDidScroll: but
user is dragging dragging is True and decelerating is False
user just ended dragging and the tableview is decelrating dragging is False and decelerating is True
programmatically called scrollForRowAtIndexPath: dragging is False and decelerating is False
So your code will be like something like this:
- (void) scrollViewDidScroll:(UIScrollView *)scrollView {
if (scrollView.dragging)
{
// Do what you want when user is dragging
} else if (scrollView.decelerating)
{
// Do what you want when table is decelerating after finished dragging
} else
{
// Do what you want when table is scrolling programmatically.
}
}
Or if you want to just distinguish the scrolling progammatically
- (void) scrollViewDidScroll:(UIScrollView *)scrollView {
if (scrollView.dragging || scrollView.decelerating)
{
// Scrolling by the user
} else
{
// Scrolling by the code
}
}
Soon after I posted the question, I took a small break, came back to the problem and figured it out. Should of done that sooner instead of wasting a few hours. bleh!
The solution is simple, set a bool flag that gets set before any progammatic scrolling and then change that after the animation is done using the event scrollViewDidEndScrollingAnimation.
bool performingAutomatedScroll = false;
- (void) scrollViewDidScroll:(UIScrollView *)scrollView {
// If we are scrolling because of code, don't use any animations.
if (performingAutomatedScroll) return;
// Animation code here.
}
- (void) scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
performingAutomatedScroll = true;
NSArray *indexPaths = [_myTableView indexPathsForVisibleRows];
[_myTableView scrollToRowAtIndexPath:[indexPaths objectAtIndex:0] atScrollPosition:UITableViewScrollPositionTop animated:YES];
[_myTableView reloadRowsAtIndexPaths:[_timeCarousel indexPathsForVisibleRows] withRowAnimation:UITableViewRowAnimationNone];
}
- (void) scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
if (!decelerate) {
performingAutomatedScroll = true;
NSArray *indexPaths = [_myTableView indexPathsForVisibleRows];
[_myTableView scrollToRowAtIndexPath:[indexPaths objectAtIndex:0] atScrollPosition:UITableViewScrollPositionTop animated:YES];
[_myTableView reloadRowsAtIndexPaths:[_timeCarousel indexPathsForVisibleRows] withRowAnimation:UITableViewRowAnimationNone];
}
}
-(void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView {
performingAutomatedScroll = false;
}

UITableView does not bounce/scroll after UISearchBar is added to view

I have a UITableView filled with rows of data. There are only 3 or 4 rows of data which do not reach the bottom of the screen, but the table still bounces vertically as expected when swiping up and down. After adding a UISearchBar to the top row/header of the UITableView in Interface Builder, the table no longer bounces initially. However, after navigating to a different view and returning to the same view, the table once again bounces. Why does the UITableView not bounce when the view is initially loaded, and how do I make it so that it does bounce?
- (void)viewWillAppear:(BOOL)animated {
[table reloadData];
[super viewWillAppear:animated];
}
- (void)viewDidLoad {
...
[table reloadData];
[super viewDidLoad];
}
- (void)viewDidUnload {
...
self.table = nil;
[super viewDidUnload];
}
I was able to solve this problem by implementing the answer on this question.
The key was to add self.tableView.bounces = YES in the viewDidLoad method.

Resources