I'm trying to figure out what my options are for having the top of a UITableView be able to stay at rest about 1/3 down the screen but still be able to scroll over the top of an image above it.
I was thinking UITableView on a UIScrollView but I haven't seen this exact thing while googling around for it.
Basically I'm looking to have a header and then the table view will scroll over the top of the header without the header moving. If I set the table view below a simple uiview or imageview, it will always be below it.
Any suggestions?
Add your header as a separate view below your table view. Your table view should completely overlap the underlying view, as if it would obscure it. Then set the table view's tableHeaderView to a transparent UIView, through which you will be able to see your underlying header view. Your table view's background colour should also be clear.
CGRect headerFrame = self.underlyingHeaderView.bounds;
UIView *transparentView = [UIView alloc] initWithFrame:headerFrame];
transparentView.backgroundColor = [UIColor clearColor];
self.tableView.tableHeaderView = transparentView;
Related
I'm trying to replicate the following GIF from Postmates checkout - a scrollable UITableView positioned on top of a MapView. This tableview can be scrolled, with the normal bounce effect if I go too far down or up.
Currently, I have the MapView and UITableView added as sibling views to my ViewController's view. I have adjusted the frame of the table view to move it down.
CGRect rect = CGRectMake(
0.f,
200.f,
self.view.bounds.size.width,
self.view.bounds.size.height - self.navigationController.navigationBar.bounds.size.height - 200.f
);
The two main issue's I'm having are:
I cannot figure out how to drag the entire tableview down when pulling down. E.g. the grey (my tableView.backgroundColor) sticks when I scroll down. If however, I make that background clear, then when I drag up, you see the map emerging from behind the view.
My cells keep disappearing when I scroll up. I have clipsToBounds = false, and I'm not actually dequeuing cells, just creating them in my cellForRow method, but they still disappear.
I feel like this should be a straightforward layout, but I'm missing something!
I've tried adjusting the contentInset of the table view, but then the scrollbar does not align with the cells as it does in the gif and does not look nice.
We needed similar effect in our app, along with parallax in the underlying view(map here/ we had a photos gallery).
I assume you want something like shown on this blogs video here
I wrote a small blog on how to achieve this. You can find this here
Basically its just a play of contentInsets and contentOffset properties of UITableView
If this does not suits you, here my suggestion in your two main points.
Controller's View has subviews
Map View (fills complete super view)
UITableView (fills complete super view) and in code set content insets.top = kHeightOfVisibleMap
The solution was simpler than I thought, no autolayout or crazy weird tricks required.
Add a full screen table view to your controller, and insert a full screen map view behind it.
self.mapView = [[MKMapView alloc] initWithFrame:self.view.bounds];
[self.view insertSubview:self.mapView belowSubview:self.tableView];
Add a dummy view with the background color of your table view with a height of around 200 pixels, and a full width, into the tableFooterView on your table view.
CGFloat footerHeight = 200.0;
UIView *dummyView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.tableView.bounds.size.width, footerHeight)];
dummyView.backgroundColor = [UIColor whiteColor];
self.tableView.tableFooterView = dummyView;
Set the content offset of the table view to be the inverse of that footer view's height:
CGFloat footerHeight = dummyFooterView.bounds.height;
self.tableView.contentOffset = CGPointMake(0, -footerHeight);
Set the content inset of your table view to offset the footer view.
self.tableView.contentInset = UIEdgeInsetsMake(footerHeight, 0, -footerHeight, 0)
Adjust the scrollbar position, again, based on the footer's height.
self.tableView.scrollIndicatorInsets = UIEdgeInsetsMake(footerHeight, 0, 0, 0);
This will push down the tableview, ensure the scroll bars match the table view, but allow it to bounce 'up' above its initial position and will ensure the background does not peek through the bottom of the tableview.
I have a UITableViewController and I put a UIView right under the navigation item and above the actual table. The problem that I have is that the view scrolls with the tableview.
How would I get it to behave exactly like the nav bar, and have the items in the tableview scroll behind it.
Rather than having the view scroll, it should remain in its position and have everything go behind it. Sorry for reiterating, but I've found thats necessary sometimes.
The view you're placing above the cell in the storyboard becomes the table view's tableHeaderView.
You can make the header view appear fixed by resetting its frame.origin to the table view's bounds.origin every time the table view lays out its subviews:
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
UIView *header = self.tableView.tableHeaderView;
CGRect frame = header.frame;
frame.origin = self.tableView.bounds.origin;
header.frame = frame;
}
Result:
Assuming you don't want the map view to move then you could set its user interaction to false.
Alternatively you could set the header of your tableView (if you only have one section) to the map view.
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.
Is it possible to add a static background to a collectionview because at the moment the background scrolls with the cells and the image looks quite bad.
my code at the moment
collectionView.backgroundView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"bg.png"]];
There are 2 ways to do what you're asking.
add the background to the UICollectionView as a subview like you're doing now and implement the scrollViewDidScroll: delegate method. Inside that method, set the frame of the background view based on the contentOffset of the collection view which will make it appear to be static on the screen. You will just need to set the frame's Y origin to be the contentOffset.y and it should work. (If there is a non-zero contentInset you may need to do a bit of additional math to get it right.)
add the background to the superview of your collection view, underneath the collection view. This is an easier and probably more efficient solution since you don't need to mess with the contentOffset at all since the background will not be in the scroll view itself.
if I understand your requirement correctly, here's how you can do it:
1) Add your static image as a subview to the parentView.
2) Set the backgroundColor of collectionView to [UIColor clearColor]
3) Add collection view as a subview to the parentView.
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.