Anthony Castelli
Anthony Castelli

Reputation: 567

Adjust a UIScrollView height based on a UITableView

I have a UIScrollView which contains a UIView and a UITableView. My goal is to adjust the height of the UIScrollView to allow me to scroll the contents of the UIScrollView to a specific point.

Here is my view: It has a UIView up top and a UITableView down below.

enter image description here

When I scroll, I want the UIView to stop at a specific point like so: enter image description here

The tableView would be able to continue scrolling, but the UIView would be locked in place until the user scrolled up and brought the UIView back to its original state.

A prime example of what I am trying to do is the AppStore.app on iOS 6. When you view the details of the app, the filter bar for Details, Reviews and Related moves to the top of the screen and stops. I hope this all made sense.

Thanks

Upvotes: 5

Views: 4098

Answers (6)

dzpqzb
dzpqzb

Reputation: 191

Just using the UITableView can solve with your problem. it is not need to use another scroll view.

  1. set your view as the header view of UITableView. Then add your present view to the header view.
  2. complete - (void)scrollViewDidScroll:(UIScrollView *)scrollView; . Tn the function to check the contentoffset of scroll view, and set the present view's frame.

Upvotes: 0

Anthony Castelli
Anthony Castelli

Reputation: 567

I ended up going with a simpler approach. can't believe I didn't see this before. I created two views, one for the UITableView's tableHeaderView and one for the viewForHeaderInSection. The view I wanted to remain visible at all times is placed in the viewForHeaderInSection method and the other view is placed in the tableHeaderView property. This is a much simpler approach, I think than using a scrollview. The only issue I have run into with this approach is all my UIView animations in these two views no longer animate. Here is my code.

[self.tableView setTableHeaderView:self.headerView];

- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
     return self.tableViewHeader;
}

Upvotes: 2

add yourself as a UIScrollViewDelegate to the UITableView and implement the - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView so that if your views are in their starter positions they do this:

- your UITableView animates its size to the second state:

 [UIView animateWithDuration:.1f animations:^{
      CGRect theFrame = myView.frame;
      theFrame.size.height += floatOfIncreasedHeight;
      myView.frame = theFrame;
    }];

- your UIView animates its vertical movement

 [UIView animateWithDuration:3 delay:0 options:UIViewAnimationOptionCurveLinear animations:^(void){
        view.center = CGPointMake(view.center.x , view.center.y + floatOfVerticalMovement);
        }completion:^(BOOL Finished){ 
        view.center = CGPointMake(view.center.x , view.center.y - floatOfVerticalMovement);]

Finally always in the delegate implement – scrollViewDidScrollToTop: so that you know can animate back to the initial state (using the same techniques reversed).

UPDATE:

since your views are inside a scroll view, there is a simpler way if you are ok with the table view being partly out of bounds in your starter position (i.e. instead of changing size it just scrolls into view):

make the scroll view frame size as big as your final tableview + your initial (entire) view and place it at 0,0 (so its final part will be hidden outside of the screen)

scrollview.frame = CGRectMake(0,0,tableview.frame.size.width,tableview.frame.size.height + view.frame.size.height);

you make the container scrollview contents as big as the entire table view + the entire view + the amount of the view that you want out of the way when scrolling the table view.

scrollview.contentSize = CGSizeMake(scrollview.frame.size.width, tableview.frame.size.height + view.frame.size.height + floatOfViewHeightIWantOutOfTheWay);

you place the view one after the other in the scrollview leaving all the additional empty space after the table view

view.frame = CGRectMake(0,0,view.frame.size.width, view.frame.size.height);

tableview.frame = CGRectMake(0,view.frame.size.height, tableview.frame.size.width, tableview.frame.size.height);

now it should just work because since iOS 3 nested scrolling is supported

Upvotes: 1

Yariv Nissim
Yariv Nissim

Reputation: 13343

What if you break the UIView into the top and bottom. The bottom will be the info.
Set UITableView.tableHeaderView = topView in viewDidLoad
and the return bottomView as Section Header in delegate method to make it float:

(UITableViewHeaderFooterView *)headerViewForSection:(NSInteger)section
{
   return bottomView;
}

Upvotes: 0

CodaFi
CodaFi

Reputation: 43330

What you're looking for is something like what Game Center happens to do with it's header which can actually be modelled with a table header, a custom section header view, and some very clever calculations that never actually involve messing with the frame and bounds of the table.

First, the easy part: faking a sticky view. That "view that's always present when scrolling the table" implemented as a section header. By making the number of sections in the table 1, and implementing -headerViewForSection:, it's possible to seamlessly make the view scroll with the tableview all for free (API-wise that is):

- (UITableViewHeaderFooterView *)headerViewForSection:(NSInteger)section {
    UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0,0,320,50)];
    label.text = @"Info that was always present when scrolling the UITableView";
    label.textAlignment = UITextAlignmentCenter;
    label.backgroundColor = [UIColor colorWithRed:0.243 green:0.250 blue:0.253 alpha:1.000];
    label.textColor = UIColor.whiteColor;
    return label;
}

Finally, the hard part: KVO. When the table scrolls, we have to keep the header up there sticky with regards to the top of the view's frame, which means that you can KVO contentOffset, and use the resultant change in value to approximate the frame that the view should stick to with a little MIN() magic. Assuming your header is 44 pixels tall, the code below calculates the appropriate frame value:

CGPoint offset = [contentOffsetChange CGPointValue];
[self.tableView layoutSubviews];
self.tableView.tableHeaderView.frame = CGRectMake(0,MIN(0,offset.y),CGRectGetWidth(self.scrollView.frame),44);

If the above is infeasible, SMHeadedList actually has a fairly great, and little known, example of how complicated it can be to implement a "double tableview". That implementation has the added benefit of allowing the "header" tableview to scroll with the "main" tableview.

For future visitors, I've implemented a much simpler version, albeit one that accomplishes the goal with Reactive Cocoa, and a little bit of a different outcome. Even so, I believe it may be relevant.

Upvotes: 0

Lefteris
Lefteris

Reputation: 14677

You can easily achieve this by setting the content size of the scrollView correctly and keep the height of the UITableView smaller than your viewcontroller's height, so that it fits the bottom part of the top UIView and the UITableView...

Another scenario is to split the top View in 2 parts. The part that will scroll away and the part that will be visible.

Then set the part that will scroll away as the entire UITableView header and the part that will remain visible as the header view for the first table section. So then you can achieve this with a single UITableView, without having to use a UIScrollView

Upvotes: 0

Related Questions