I have the following layout in my view controller. I want to be able to scroll vertically with the header scrolling off the view and the UISegmentedControl sticking to the top of the view, beyond that the remaining scroll should be handled by the Collection View.
However I'm a bit confused as to what is the best approach to implemented this layout.
I tried a few implementations with mixed results:
UIScrollView with UICollectionView as subviews: UIScrollView as the parent view with the header, segmented control and collection views as child controls. The problem with this approach is that the nested scrolling does not seem to work correctly. To be able to scroll the UIScrollView the tap needs to be outside the CollectionView area otherwise only the CollectionView scrolls and the header and segmented control don't move.
Header and Segmented Control in Header cell: I tried another approach by using a single CollectionView. I added the header and Segmented Control as subviews of a single Header cell of the collection view. When the segmented control value was changed, I switch the data source property of the CollectionView to achieve the 3 views required for the collection view. Visually everything works perfectly. The only problem here is the race condition when switching quickly between first,second and third tabs. I load the data from a web service, if the web service takes time and is still loading the data and I quickly switch the tabs then I run into bugs where the data returned is for a different collection view than what is currently selected, a lot of out of order sync issues.
Update constant value for Autolayout Constraint: Another approach I tried is to change the constant value of the auto layout constraint applied to "Header" view. Then I added a gesture to the view controller's view to track the scroll, as the user scrolls vertically I adjust the constant of the auto layout constraint so that the "header" cell pops out of view. Again this doesn't seem to work that smoothly, but I suppose I can tweak it, but it seems sort of a hack.
Is there a better way to implement this layout?
#2 seems like a good solution — the scrolling gestures will be most consistent with what users expect, since it's all a single scroll view. (I agree that #3 sounds like a hack.) You can make the header "sticky" with some custom layout attributes.
The only problem here is the race condition when switching quickly between first, second and third tabs.
This is a common problem with asynchronous loading when views are being switched out (especially when you are loading data into individual cells, which are being reused as you scroll). It is important that upon receiving the data you always check whether the receiver is still expecting it; i.e., you should check the segmented control value before changing the backing data source. You could also:
Use separate data source objects for the different segments, having each one manage its own data fetching so they can't get mixed up.
Cancel the outstanding requests, if you can, when quickly switching tabs, to avoid unnecessary network requests.
Cache data to avoid re-fetching every time you switch tabs.
I think you want the same functionality that pinterest profile page have. To implement such functionality at easy way, you need to do following things.
Step 1 : Add UIView as tableHeaderView those who showing off while scrolling up.
self.tableHeaderView = yourView
Step 2 : Add UISegmentControl in section header view.
- (UITableViewHeaderFooterView *)headerViewForSection:(NSInteger)section{
return your_segmentcontrolView;
}
Step 3 : Add UICollectionView into first row of first section.
By implementing following way, you can got your desire functionality.
Hope this help you.
An alternative approach you could consider:
Use a UITableView to contain your UI
Create a UITableView, and set your header as the UITableView's headerView.
Use a sectionHeader to contain the segmentedControl.
Place your collectionView inside of a single UITableViewCell. Or alternatively, you may be able to use the UITableView's footerView to contain the gridView.
By using the sectionHeader, this should allow the header to scroll out of view, but then the sectionHeader will stick below the navigationBar or top of the contentView until another section comes into view (and in your case you will only have one section.)
Add Header View, Body View (Holding Segment View & Collection View) into scroll view.
Initially set userInteractionEnabled property to "NO" for collection view.
Track the insect of scroll view always.
If the y-coordinate of the scrolled insect is more than the height of header view, then set userInteractionEnabled property to "YES" so that thereafter collection view can be scrolled.
If user scroll outside the scroll view and try to bring the header view down, i.e Scroll view y-coordinate insect is less than the height of header view, then immediately change the user iteration mode of collection view and allow user to scroll the scroll view till the top.
Rather than implementing this by hand, you could use a library/cocoapod to set this up for you. This one looks like a pretty good fit: https://github.com/iosengineer/BMFloatingHeaderCollectionViewLayout
Plus, the code is open-source, so you can always modify as needed.
All I can say is that you need to subclass UIView and make it a delegate of UIGestureRecognizerDelegate and UICollectionViewDelegate, then in your UIView subclass, do the following, I can't give out anymore information on this because the code, although owned by myself, is proprietary to the point of probably enraging quite a few organizations that I've used this for, so here's the secret sauce:
CGPoint contentOffset = [scrollView contentOffset];
CGFloat newHeight = [_headerView maxHeight] - contentOffset.y;
CGRect frame = [_headerView frame];
if (newHeight > [_headerView maxHeight]) {
frame.origin.y = contentOffset.y;
frame.size.height = [_headerView maxHeight];
[_headerView setFrame:frame];
} else if (newHeight < [_headerView minHeight]) {
frame.origin.y = contentOffset.y;
frame.size.height = [_headerView minHeight];
[_headerView setFrame:frame];
} else {
frame.origin.y = contentOffset.y;
frame.size.height = newHeight;
[_headerView setFrame:frame];
}
if ([_delegate respondsToSelector:#selector(scrollViewDidScroll:)]) {
return [_delegate scrollViewDidScroll:scrollView];
}
You must subclass another UIView that is defined as the header for this custom UiCollectionView. Then, you must declare the UIView custom header view inside the custom subview of the UIView/UICollectionView delegate, and then set the header of that custom subview inside the UICollctionViewdelegate. You should then pull in this compounded subclass of UIView/UIcollectionView into your UIViewController. Oh yes, and in your layoutSubViews, make sure you do the height calculations that are passed through a double layered subclass. So, you will have the following files:
UIVew this is the delegate of UICollectionView and what I mentioned before
UIView this is a UISCrollViewDelegate and this is the header view
UIViewController that pulls in the subclassed UIView in number 1
UIView subclass of number 1 that pulls in number 2 and sets it as its header
In the number 4 part, make sure you do something like this:
- (CGFloat)maxHeight
{
if (SCREEN_WIDTH == 414)
{
return 260;
}else if (SCREEN_WIDTH == 375)
{
return 325;
}else
{
return 290;
}
}
- (CGFloat)minHeight
{
if (SCREEN_WIDTH == 414)
{
return 90;
}else if (SCREEN_WIDTH == 375)
{
return 325;
}else
{
return 290;
}
}
This will then pass through to the UIView subclass that is a compounded subclass as I already explained. The idea is to capture the maxHeight of you header in the subclass of this header UIView (number 2 above), and then pass this into the main UIView subclass that intercepts these values in the scrollViewDidScroll.
Last tidbit of information, make sure you set up your layoutSubviews in all methods to intercept scroll events. For example in number 1 above, the layoutsubviews method is this:
- (void)layoutSubviews
{
CGRect frame = [_headerView frame];
frame.size.width = [self frame].size.width;
[_headerView setFrame:frame];
[super layoutSubviews];
}
This is all I can give you, I wish I could post more, but this should give you an idea of how it's done in production environments for the big time apps you see out in the wild.
One more thing to note. When you start going down the road of intense implementations like this, don't be surprised to learn that, for example, a single view controller in an app that works with methods like I've explained will have anywhere from 30-40 custom subclasses that are either subclasses in their own right or compounded subclasses or subclasses of my own subclasses or my own subclasses. I'm telling you this so you get an idea of how much code is required to get this right, not to scare you, but to let you know that it might take a while to get right, and to not kick yourself in the butt if it takes awhile to make work. Good luck!!
Related
I have two table views set up side by side, and I need them to scroll at exactly the same time. So, when you scroll one, the other one will scroll at the same time.
I did some searching and I couldn't find any information, but I assume it must be possible somehow.
My table views are both connected to the same class and I differentiate between them like this:
if tableView == tableView1 {
//
} else if tableView = tableView2 {
//
}
You can get set the scrollView delegate to self on both of your tableView's scrollViews. And in -scrollViewDidScroll, take the contentOffset and set the other scrollView's contentOffset to the same value.
Like Schemetrical said you should use scrollViewDidScroll.
see the first answer of this:
Scrolling two UITableViews together
- (void)scrollViewDidScroll:(UIScrollView *)scrollView;
{
UITableView *slaveTable = nil;
if (self.table1 == scrollView) {
slaveTable = self.table2;
} else if (self.table2 == scrollView) {
slaveTable = self.table1;
}
[slaveTable setContentOffset:scrollView.contentOffset];
}
If you want them to scroll in prefect lock-step then this isn't a trivial problem. UITableView is a subclass of UIScrollview, so you could probably create a custom subclass of UITableView that overrode various UIScrollView methods and when something caused the table view to scroll, it would do the same thing to the other table view.
Edit: #Schemetrical's suggestion of using the scroll view delegate is cleaner than creating subclasses. You might have to monitor quite a few of the scroll view delegate methods and use them to match the behavior in the other scroll view.
EDIT #2:
Apparently I'm wrong and scrollViewDidScroll is called for every change in the scroll view, so it's simpler than I thought to keep them synced. I'm going to leave my answer for context even though I was wrong.
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?
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.
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;
}
In an application that I've worked, we needed something close to that, we wanted to have a uitableview inside the first cell of another uitableview, which should be scrollable (you can check the app here)
Our approach was quite different, and we thought about doing it your way, but that way was a bit overkill for the time we had, and needed too much custom control instead of using what apple already gives us.
I'll share our method just for you to have another way to solve your problem, since I can't figure out what is wrong in that method call.
On the outer tableview's datasource lifecycle, we were able to know how our inner table view would be, allowing us to feed our inner table view with all the data, which would then gave us the total content size of the inner table view.
With this info, and since we knew (and in your case, you know) the actual cell where the inner table view would be placed, we make that cell's height equal to the height of the inner tableview content size. With this, the inner table view is 'totally visible'(in your case, the collection view) inside the outer table view cell, but since the cell is so big, it looks like we are scrolling the inner table view when we are simply scrolling a really big, special, cell.
And it won't bounce, if you don't want it too.
In our case, this worked, and the implementation was pretty straight forward.
In your case, and since you have a UISegmentedController, you should do a beginUpdates endUpdates and a 'reloadData', upon press in the UISegmenedController, in your outer table view in order to be able to recalculate the collection view's content size and resize the last cell, but that is pretty straight forward.
I know I'm not answering your question, but this method allowed us to achieve something so similar in such a fast and straight forward way that I thought it was worth sharing.
Hope it helps.
I have two questions related to UITableViews.
1) The first one is, what is the gap at the top of the UITableView? My app always starts with the top cell not flush with the top of the tableview (as shown in the second image), it starts about one cell lower, i.e. where that gap is in the interface builder. I can't find where that is coming from, or why.
2) I can't seem to resize the uitableview programmatically, I'm trying to reduce the height after a popup appears. I've shown an example of it not working in the second picture.
Here is (an example of) what I am trying at the moment:
- (void)viewDidLoad
{
[super viewDidLoad];
self.table_view.delegate = self;
CGRect tableBounds = self.table_view.bounds;
tableBounds.size.height -= 100;
self.table_view.bounds = tableBounds;
CGRect tableFrame = self.table_view.frame;
tableBounds.size.height -= 100;
self.table_view.frame = tableFrame;
}
Thanks!
UITableView Selected:
Simulation:
In your xib (or storyboard) put your UITableView at position (0,0). ( the same position as the navigation bar).
The first image shows that your table view has problems even in Interface Builder. It looks as if you've set the top inset incorrectly; check your edge insets.
The reason your resizing code is not working is probably that it is too early (viewDidLoad); put it in viewDidAppear: and see if that works, and if it does, try moving it back to viewWillAppear: so the user doesn't see the resizing. If it doesn't work, it might be because you're using auto layout; you can't manually alter the frame of something whose frame is dictated by auto layout. (Your resizing code is also silly; you want to set the frame, not the bounds.) But it might also be because you're using a UITableViewController in a UINavigationController; if you do that, the table view is under the navigation controller's direct control and its size is not up to you.
What I have in the view controller view are :
An image of fixed height
Few labels
Table view with n rows.
Once rendered I want everything here to be inside the scroll the view so the user can scroll the entire screen as needed. Note that the scrollView needs to expand to the entire size of the tableView to show its full contents. I have tried different ways of doing this but unable to do it. I would appreciate any pointers or code segment to get this done.
There are essentially two ways to do so.
tableHeaderView
The first way involves the tableHeaderView property of the UITableView instance you have. You can simply add the UITableView with the constraints/frame/autoresizingMask that allows you to put it full-screen. Done that, you simply do (i.e. in your viewDidLoad):
UIView *headerView = [UIView new];
// Here I am supposing that you have a 200pt high view and a `self.tableView` UITableView
headerView.frame = CGRectMake(0, 0, self.tableView.frame.size.width, 200.0);
UIImageView *fixedImageView = [UIImageView new];
// configure your imageView..
[headerView addSubview:fixedImageView];
// configure labels as you want and add them to headerView as subviews
// Now set `UITableView` headerView
self.tableView.tableHeaderView = headerView;
If you want to use AutoLayout for your tableHeaderView, I suggest you to take a look at this question
Dynamic scrollView
Another way to do this is to to create an UIScrollView, put everything inside, and let it scroll. The downside of this method is that if you are using floating section headers for your UITableView, they will not float due to the fact that the tableView is going to stay fixed, while the parent scrollView is going to scroll.
On the other side, this approach is more AutoLayout friendly due to the fact you can use constraints easily.
To do so, you start adding an UIScrollView to your view, and placing all your other views inside it.
Be sure to add a Vertical Spacing constraint between the first view inside your scrollView (I suppose the UIImageView) and the scrollView top, and between the last view (I suppose the UITableView) and the scrollView bottom, to avoid an ambiguous content size.
You should have something like that (I omitted the labels for the sake of brevity):
Note that every view is inside a parent UIScrollView
After that, add an Height constraint to the tableView, and add an IBOutlet to your view controller subclass, i.e. like this:
#property (weak, nonatomic) IBOutlet NSLayoutConstraint *tableViewHeightConstraint;
Now you only need to configure this constraint to reflect the tableView natural height, given by its rows, etc. To do so, you simply calculate the height in this way:
// Resize TableView
CGFloat height = self.tableView.contentSize.height;
self.tableViewHeightConstraint.constant = height;
Now the tableView will resize, and due to its constraints it will adapt the parent scrollView contentSize.
Just be sure to refresh this height constraint anytime you reload the UITableView dataSource.
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.
When I scroll, I want the UIView to stop at a specific point like so:
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
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;
}
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
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
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.
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;
}
Just using the UITableView can solve with your problem. it is not need to use another scroll view.
set your view as the header view of UITableView. Then add your present view to the header view.
complete - (void)scrollViewDidScroll:(UIScrollView *)scrollView; . Tn the function to check the contentoffset of scroll view, and set the present view's frame.