Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

UICollectionView Within Last UITableViewCell AND Congruent Scrolling? How?

I'm racking my head over this one, I know this has to be possible but after going over every bit of documentation I still can't come up with something that works well.

Basically, I have a UICollectionView in the LAST cell of a UITableView. What I want to happen is, only when the UITableView is totally scrolled to the bottom, can the UICollectionView in it's last cell start scrolling. And, if the TableView offset reaches the bottom of the CollectionView's tableviewcell during a drag / pan, any additional dragging of the current table view drag / pan should effect the collection view instead of the table view.

Also, when the collection view is scrolling, if a user starts scrolling on the collection view, if the collection view reaches the top of it's scroll (Content Y offset of 0 or less), and additional scrolling of the current pan/drag gesture in affect should cause the containing table view to scroll up.

The reason I want to achieve this effect, is because the table view cell above the last cell containing the collection view, contains a UISegmentControl that toggles the contents of the UICollectionView, and I want the user to be able to toggle this segment at any time while scrolling in the CollectionView. Meaning the collection view has to scroll but the parent table view needs to not scroll..

I've tried playing with the gesture recognizers, and doing something with

– gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:

This gets me halfway there, I can use a flag to return yes/no to this method if the collection view is to scroll into a negative offset (Past it's top offset) or if the table view has reached the end of it's total scroll.. I get sort of close to what I want to achieve, but if I scroll up slowly on the collection view, it doesn't fire the simultaneous recognizer, same happens if I scroll down on the table view too slowly.

Another issue is, I do NOT want the collection view to bounce. However setting bounces to NO totally prevents the simultaneous recognizer to fire at all. I even tried setting content offset to CGPointZero on the collection view in it's viewDidScroll if it's y offset were to dip below zero. This also doesn't work and prevents the simultaneous method from firing...

Does anyone have any idea what to do? Or something to point me in the right direction?

UPDATE -

Still trying at this, I've made little progress towards the behavior I'm trying to achieve. I've messed with toggling userInteractionEnabled in the viewDidScroll method, as have I tried in willBeginDragging. The same with scrollEnabled property.. No luck :( I get a behavior similar to what I want with this, however the parent view will not scroll up until the user lets off the screen and attempts to scroll again..

UPDATE -

Is there anyway to transition the panGestureRecognizer currently handling scroll events DURING scrolling? If I could transition the scroll handler from the child to the parent while still scrolling this would solve my issue. I've looked through apple's gesture related and uiscrollview related documentation and can't find anything close to doing that.

UPDATE -

Just got done trying something like this..

- (CGPoint)maxParentContentOffset
{
    return CGPointMake(0, self.parentScrollView.contentSize.height - self.frame.size.height - 44);
}

- (void)parentScrollViewDidScroll:(UIScrollView *)parentScrollView
{
    if (self.contentOffset.y > 0) {
        self.parentScrollView.contentOffset = [self maxParentContentOffset];
    }
}

- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
    if (scrollView.contentOffset.y < 0) {
        scrollView.contentOffset = CGPointZero;
    }
}

- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
{
    CGPoint translation = [scrollView.panGestureRecognizer velocityInView:scrollView.superview];
    if (translation.y < 0) {
        [UIView animateWithDuration:0.5f animations:^(void) {
            self.parentScrollView.contentOffset = [self maxParentContentOffset];
        }];
    }
}

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
    return YES;
}

However there is a problem... for some reason I keep getting a bad access error on the following method

- (void)parentScrollViewDidScroll:(UIScrollView *)parentScrollView
    {
        if (self.contentOffset.y > 0) {
            self.parentScrollView.contentOffset = [self maxParentContentOffset];
        }
    }

Specifically setting the content offset. Which is really strange, because with breakpoints I'm seeing parentScrollView and self as being set. I'm wondering if it's not a bad access but it's getting trapped in an infinite loop for some reason? Any ideas?

like image 580
Braydon Batungbacal Avatar asked Jul 04 '14 18:07

Braydon Batungbacal


2 Answers

Even though you may manage to make it work now, embedding a collection view inside a table view (both UIScrollView subclasses) is not a good idea and it will bug as soon as apple modifies their implementation.

Try to migrate to a single UICollectionView layout. After all there's nothing you can't achieve with a collection view that a table view can.


Separate your "table view" and "collection view" in two (or more) collection view sections, then implement layoutAttributesForItemAtIndexPath: differently according to the indexPath.section.

To make it table view-like you'll want to return frames whose width are the same as the collection view.

If your layout is simpler then you could use a UICollectionViewFlowLayout (maybe your already are) and implement collectionView:layout:sizeForItemAtIndexPath: as described above.

like image 153
Rivera Avatar answered Nov 16 '22 02:11

Rivera


Figured this out after a good 8 hours.. I had a confliction due to infinite setting of the parent scroll view offset, since I had multiple objects that were of the same class that received a call whenever their parent view scrolled, both trying to set the same parent view offset to zero, which caused the other class to see scroll changing, and calling their method to change offset, and the process happening infinitely causing a crash.

This code however, solved everything and functions exactly as I was desiring. Hopefully this helps anyone else in the future looking to get congruent scrolling between a parent and child scroll view.

- (CGPoint)maxParentContentOffset
{
    return CGPointMake(0, self.parentScrollView.contentSize.height - self.frame.size.height - 44);
}

- (void)parentScrollViewDidScroll:(UIScrollView *)parentScrollView
{
    if (self.contentOffset.y > 0 && self.isDragging) {
        self.parentScrollView.contentOffset = [self maxParentContentOffset];
    }
}

- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
    if (scrollView.contentOffset.y < 0) {
        scrollView.contentOffset = CGPointZero;
    }
}

- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
{
    CGPoint translation = [scrollView.panGestureRecognizer velocityInView:scrollView.superview];
    if (translation.y < 0) {
        [UIView animateWithDuration:0.5f animations:^(void) {
            self.parentScrollView.contentOffset = [self maxParentContentOffset];
        }];
    }
}

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
    return YES;
}
like image 24
Braydon Batungbacal Avatar answered Nov 16 '22 03:11

Braydon Batungbacal