Locking a UISearchBar to the top of a UITableView - ios

I want to get the behavior like this:
I have a UISearchBar, which is a table header view of my tableview. When scrolling the table, the search bar does indeed move, but if you scroll above the boundaries of the table, the search bar never stops touching the navigation bar.
I found a good answer here - Locking a UISearchBar to the top of a UITableView like Game Center
But it not works on iOS 6 - manipulations with table view header frame don't work
What can be the reason of this?

I found a solution, which works on iOS 6 and lower
Make a subclass of UITableView and override layoutSubviews method
- (void)layoutSubviews
{
[super layoutSubviews];
CGRect rect = self.tableHeaderView.frame;
rect.origin.y = MIN(0, self.contentOffset.y);
self.tableHeaderView.frame = rect;
}

If you use the following:
[_tableView setTableHeaderView:_searchBar];
When you scroll the table view beyond the first row, the search bar should also disappear rather than sticking to the top of the view.
Without seeing your code, I can only assume that perhaps you have added the UISearchBar as your section header rather than the table header?

That because iOS6 sdk not update when scrolling, maybe same optimize. so need to call tableview layoutSubviews when scroll.
-(void)scrollViewDidScroll:(UIScrollView *)scrollView
{
UISearchBar *searchBar = searchDisplayController.searchBar;
CGRect rect = searchBar.frame;
rect.origin.y = MIN(0, scrollView.contentOffset.y);
[scrollView layoutSubviews];
searchBar.frame = rect;
}

Related

iOS 8 - Initially hide a UITableView's headerView from view

Just like in the native iOS Mail app, when I push a UITableViewController onto a UINavigationController, I would like to make it so that the UITableView initially appears slightly scrolled downwards, obscuring its headerView beneath the navigation controller's navigation bar.
At the same time, even if the height of all of the cells is smaller than the height of the table view, it should be possible for the user to scroll up and down to explicitly show or hide the header view again.
With that logic, it would appear that there are two considerations to make for this implementation:
1) Ensuring that the minimum content size of the table view is at least the height of the table view's frame + the height of the header view.
2) When the table view is initially presented, the content offset is incremented by the height of the header view.
I've tried manually setting both the contentOffset and contentSize properties of the table view in 'viewWillAppear', however this appears to have no effect (It's possible the table view is getting reloaded after that point). Trying to set them in 'viewDidAppear' will work, but that's too late as it only gets called once the 'push' animation has completed.
While this sort of question has been asked before for previous iOS versions, I was unable to get any of them working in iOS 8. Additionally, they all dealt with changing the offset, but not the contentSize of the table view.
Has anyone gotten this sort of behavior working in iOS 7 and/or 8 before?
Update - (30/1/2015)
Alright. This wasn't sitting well with me last night, so I had another play with it, and I found a MUCH better and cleaner solution.
I discovered that the tableView property of UITableViewController is NOT readonly. So it actually makes more sense to simply manage the contentSize property in a UITableView subclass and then assign that subclass back to the UITableViewController.
#implementation TOCustomTableView
- (void)setContentSize:(CGSize)contentSize
{
CGFloat scrollInset = self.contentInset.top + self.contentInset.bottom;
CGFloat height = (CGRectGetHeight(self.bounds) - scrollInset) + CGRectGetHeight(self.tableHeaderView.frame);
contentSize.height = MAX(height, contentSize.height);
[super setContentSize:contentSize];
}
#end
---
#implementation TOCustomTableViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.tableView = [[TOCustomTableView alloc] initWithFrame:self.view.bounds style:UITableViewStylePlain];
}
#end
This way, the table view's minimum contentSize is always explicitly set to be the height of the table view + the headerView size, achieving the desired effect with zero jittering. :)
Original Answer
trick14 pointed me in the right direction. So the correctly functioning code I ended up with.
- (void)resetTableViewInitialOffset
{
CGPoint contentOffset = self.tableView.contentOffset;
contentOffset.y = self.tableView.contentInset.top + CGRectGetHeight(self.headerView.frame);
self.tableView.contentOffset = contentOffset;
}
- (void)resetTableViewContentSize
{
CGSize contentSize = self.tableView.contentSize;
CGFloat scrollInset = self.tableView.contentInset.top + self.tableView.contentInset.bottom;
CGFloat height = (CGRectGetHeight(self.view.bounds) - scrollInset) + CGRectGetHeight(self.headerView.frame);
contentSize.height = MAX(height, contentSize.height);
self.tableView.contentSize = contentSize;
}
- (void)viewDidLayoutSubviews
{
[super viewDidLayoutSubviews];
if (!self.headerBarInitiallyHidden) {
[self resetTableViewContentSize];
[self resetTableViewInitialOffset];
self.headerBarInitiallyHidden = YES;
}
}
I'm also making sure to call 'resetTableViewContentSize' each time I perform a 'reloadData' on the table view as well.

Fix a UIView(from storyboard) to the top of a UITableView

I've attached a UIView from the storyboard to the top of my UITableView and linked it to the code as IBOutlet, and I want it to be fixed to the top of my tableView. I've tried several ways:
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
headView.center = CGPointMake(headView.center.x + scrollView.contentOffset.x, headView.center.y + scrollView.center.y);
[scrollView bringSubviewToFront:headView];
}
and
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
CGRect newFrame = headView.frame;
newFrame.origin.y = 0;
[headView setFrame:newFrame];
}
and some other ways I found in the site, but none worked for me, I affraid because I connected headView from the storyboard.
How can I do it? many thanks!
Sounds like you have a UITableViewController and have added the view to the table views header view, hence it scrolls with the table.
If this is the case try changing your main view controller to a UIViewController and add the view and tableview there.

UISearchBar hides view in iOS 7

When I initiate a search on UISearchBar which is a child off a UITableView, any other view except the UITableView becomes hidden. This issue only happens on iOS 7. And I dont have any specific code which hides the other views.
To come to the solution I first had to figure out the issue.
When text is input into the UISearchBar, it creates a UITableView which sits on top of the parent view.
To show the hidden parent view, the UITableView created must be offset and re sized to fit within a smaller area.
(void)searchDisplayController:(UISearchDisplayController *)controller didShowSearchResultsTableView:(UITableView *)tableView {
// The tableView the search tableView replaces
CGRect f = self.mainTableView.frame;
CGRect s = self.searchDisplayController.searchBar.frame;
CGRect updatedFrame = CGRectMake(f.origin.x,
f.origin.y + s.size.height,
f.size.width,
f.size.height - s.size.height);
tableView.frame = updatedFrame;
}
Try to add this in the viewDidLoad for the popover UIViewController:
if ([self respondsToSelector:#selector(edgesForExtendedLayout)])
self.edgesForExtendedLayout = UIRectEdgeNone;

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.
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.

How to set up my iOS storyboard to bounce like Instagram Stream?

I am building an app with a stream of social content and am trying to get the behavior of how instagram does it's stream in app. So basically a top header that scrolls off the screen but bounces between that and the content. I can make the top header scroll off the screen and I can make the view not bounce but I want to use Pull to refresh and that ends up going above the "faux" nav bar UIView. I know that a normal Navbar will produce this but this one that scrolls off is a different story.
Currently I have a UITableview that has a UIView above the UITableViewCell and everything works great except the bounce happens above the UIView. I figure I need to get the UIView above the UITableView however in the UITableViewController the storyboard won't allow me to place the UIView above the UITableView.
Any ideas???
Thanks
Well I finally got this all to work so I thought I would post the Answer for everyone.
Basically I set a standard Navigation bar and then on scrollViewDidScroll I get the offset of the scrolling and change the frame based on that. This seems to work perfectly, see below for my scrollViewDidScroll method.
- (void)scrollViewDidScroll:(UIScrollView *)sender {
//Initializing the views and the new frame sizes.
UINavigationBar *navbar = self.navigationController.navigationBar;
UIView *tableView = self.view;
CGRect navBarFrame = self.navigationController.navigationBar.frame;
CGRect tableFrame = self.view.frame;
//changing the origin.y based on the current scroll view.
//Adding +20 for the Status Bar since the offset is tied into that.
navBarFrame.origin.y = MIN(0, (sender.contentOffset.y * -1)) +20;
navbar.frame = navBarFrame;
tableFrame.origin.y = MIN(0,MAX(-44,(sender.contentOffset.y * -1)));
tableView.frame = tableFrame;
}
Also you will want to make your TableView 44px taller to compensate for the scrolling otherwise your frame will not be big enough. I just did this in viewWillAppear and made the frame bigger.

Resources