How to implement the scroll process of tableheaderview? - ios

When I add some hidden header view, like search bar, to the table view, it scrolls automatically to content's top or table cell's top, when I set the offset of the scroll to the middle of a search bar. (I used below code)
// in viewDidLoad section
UISearchBar *searchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(0, 0, 320, 44)]
[self.tableView setTableHeaderView:searchBar]
// in viewWillAppear: section
[self.tableView setContentOffset:CGPointMake(0, 44)];
For example, if I scroll up a little amount when the search bar is hidden, it automatically scrolls to display the entire search bar.
And if I scroll down a little when the search bar is displayed, it automatically scrolls to make the search bar hidden.
I used below code to implement this feature to my collection view's header, but that wasn't exactly the same as the table view's feature.
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
{
CGFloat offset = scrollView.contentOffset.y;
if (offset > 22 && offset < 44) {
[scrollView setContentOffset:CGPointMake(0, 44) animated:YES];
} else if (offset > 44) {
return;
} else {
[scrollView setContentOffset:CGPointMake(0, 0) animated:YES];
}
}
I think it is very hard to mimic above feature exactly, because the judgment of displaying the entire search bar or hiding the search bar is very subtle.
So my question is, "Is there any pre-implemented method in iOS SDK, or in UICollectionViewController?".
I googled for many hours but I couldn't found the answer.

I couldn't find the pre-implemented way to implement that feature, but the below way seems to make things similar to that of table view's.
First, add CGFloat type ivar originScrollOffset.
Then, I used the below code to implement scroll view delegate.
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
{
originScrollOffset = scrollView.contentOffset.y;
}
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
{
CGFloat endScrollOffset = scrollView.contentOffset.y;
NSLog(#"%f %f", originScrollOffset, endScrollOffset);
if (endScrollOffset <= 44 && endScrollOffset >= 0) {
if (originScrollOffset >= 44) {
if (originScrollOffset - endScrollOffset > 10) {
originScrollOffset = 0;
[self.collectionView setContentOffset:CGPointMake(0, 0) animated:YES];
} else {
originScrollOffset = 44;
[self.collectionView setContentOffset:CGPointMake(0, 44) animated:YES];
}
} else {
if (endScrollOffset - originScrollOffset > 10) {
originScrollOffset = 44;
[self.collectionView setContentOffset:CGPointMake(0, 44) animated:YES];
} else {
originScrollOffset = 0;
[self.collectionView setContentOffset:CGPointMake(0, 0) animated:YES];
}
}
}
}

Related

Customizing UIView class with UIScrollview and UITableview

I want to add three views in a UIViewController,
UIImageView
Scrollable Segment control (HMSegmentedControl)
UIViews (or) UITableviewCells
it looks like this,
When the whole view scrolls the segment control should pin at the top of the screen (like header in UITableview).
My code
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
if ((long)scrollView.tag == 10) {
// Main scrollview
if (self.lastContentOffset >= scrollview.contentOffset.y) {
NSLog(#"Down");
if (scrollview.contentOffset.y < 158.0f) {
[scrollview setContentOffset:CGPointMake(scrollview.contentOffset.x, scrollview.contentOffset.y)];
[matchInfoTable setScrollEnabled:NO];
[scrollview setScrollEnabled:YES];
}
} else if (self.lastContentOffset <= scrollview.contentOffset.y) {
NSLog(#"Up");
NSLog(#"%f",scrollview.contentOffset.y);
if (scrollview.contentOffset.y > 138.0f) {
[scrollview setScrollEnabled:NO];
[matchInfoTable setScrollEnabled:YES];
}
if (scrollview.contentOffset.y >= 163.0f) {
[scrollview setContentOffset:CGPointMake(scrollview.contentOffset.x, 163.0f)];
[scrollview setScrollEnabled:NO];
[matchInfoTable setScrollEnabled:YES];
}
}
self.lastContentOffset = scrollview.contentOffset.y;
NSLog(#"LastOffset :: %f",self.lastContentOffset);
} else if (scrollView.tag == MATCH_INFO) {
// match info table
if (scrollView.contentOffset.y == 0) { // TOP
[scrollview setContentOffset:CGPointMake(scrollview.contentOffset.x, scrollview.contentOffset.y)];
[matchInfoTable setScrollEnabled:NO];
[scrollview setScrollEnabled:YES];
}
}
}
In this code when the segment control pin at the top. The below view is not scrolling continuously. I need to trigger its scrolling again.
All answers are appreciated!!
I think better you should use UIPageViewController and put inside all tableviews, then you can detect swipes and change tabs.
Also you can use UISwipeGestureRecognizer and detect left and right swipes described in this post left and right swipes

Hide NavigationControllerBar when scrolling the tableview

I have implemented a UITableViewController.
The first section is a large image. When the view controller appeared initially, I set the navigationBar to be translucent.
When the tableview is scroll down, the navigationBar.translucent is set to NO and the tableview content frame is moved upwards so that the first section is out of the screen. I implemented the movement in the scrollview delegate :
-(void)scrollViewDidScroll:(UIScrollView *)scrollView
When the tableview is scroll up, the navigation becomes translucent again and the tableview frame is restored.
The problem is, when the scrollview delegate catches the scroll gesture. Once the tableview and navigationBar begins the animation. The scroll action of the tableview stops. Therefore if I want to scroll the tableview to bottom I have to scroll twice, the first time animates the frames and then scroll again, and I think it can be enhanced.
Here is the code.
-(void)scrollViewDidScroll:(UIScrollView *)scrollView
{
if (scrollView == _subTable) {
NSIndexPath * indexPath ;
CGFloat offset = scrollView.contentOffset.y;
if ((offset - currentOffset)>40) {
if (!scrollAnimate) {
self.navigationController.navigationBar.translucent = NO;
[UIView animateWithDuration:0.5 animations:^{
[_mainTable setContentOffset:CGPointMake(0, headHeight) animated:YES];
[_subTable setFrame:CGRectMake(CGRectGetWidth(self.menuTable.frame) , 0, kScreen_Width/3.5*2.5, kScreen_Height-schedualHeight-48)];
[_mainTable setFrame:CGRectMake(0, 0, kScreen_Width, _mainTable.frame.size.height+headHeight)];
[_menuTable setFrame: CGRectMake(0, 0, kScreen_Width/3.5, _mainTable.frame.size.height+headHeight-48)];
}];
if (CGRectGetMaxY(_checkOutBar.frame)!= kScreen_Height-44)
{
[_checkOutBar setFrame:CGRectOffset(_checkOutBar.frame, 0, -44)];
}
scrollAnimate = !scrollAnimate;
frameOffset = !frameOffset;
[_mainTable reloadData];
_checkOutBar.tag = 1000;
}
}
else if((offset - currentOffset)<-40)
{
if (scrollAnimate) {
self.navigationController.navigationBar.translucent = YES;
[UIView animateWithDuration:0.5 animations:^{
[_mainTable setFrame:CGRectMake(0, 0, kScreen_Width, kScreen_Height+self.navigationController.navigationBar.frame.size.height+headHeight)];
[self.mainTable setFrame:CGRectOffset(_mainTable.frame, 0, -(self.navigationController.navigationBar.frame.size.height))];
[_subTable setFrame:CGRectMake(CGRectGetWidth(self.menuTable.frame), 0, kScreen_Width/3.5*2.5, tableHeight)];
[_menuTable setFrame:CGRectMake(0, 0, kScreen_Width/3.5, tableHeight)];
}];
scrollAnimate = !scrollAnimate;
frameOffset = !frameOffset;
[_mainTable reloadData];
if (CGRectGetMaxY(_checkOutBar.frame)!= kScreen_Height) {
[_checkOutBar setFrame:CGRectOffset(_checkOutBar.frame, 0, 44)];
}
_checkOutBar.tag = 2000;
}
}
if ((offset - currentOffset)>0)
{
indexPath = [[_subTable indexPathsForVisibleRows]lastObject];
}
else
{
indexPath = [[_subTable indexPathsForVisibleRows]firstObject];
}
if (indexPath) {
if (indexPath.row == 0) {
selected = indexPath.section;
[_menuTable reloadData];
}
}
currentOffset = offset;
}
}
From Apple documents. set NavigationController. hidesBarsOnSwipe = YES;
hidesBarsOnSwipe
Property
A Boolean value indicating whether the navigation bar hides its bars in response to a swipe gesture.
Declaration:
SWIFT:
var hidesBarsOnSwipe: Bool
OBJECTIVE-C:
#property(nonatomic, readwrite, assign) BOOL hidesBarsOnSwipe
Discussion:
When this property is set to YES, an upward swipe hides the navigation bar and toolbar. A downward swipe shows both bars again. If the toolbar does not have any items, it remains visible even after a swipe. The default value of this property is NO.
Availability:
Available in iOS 8.0 and later.

How to pass event from outer scroll to inner scroll in a single move

I have two scrollViews i.e one inside other,
-OuterScroll
----InnerScroll
Need Outer scrollView to stop automatically and inner starts in a single scroll when outer ScrollView reaches contentOffSet value of greater than 300.
So far..
-(void)scrollViewDidScroll:(UIScrollView *)scrollView{
[self.view endEditing:YES];
if (scrollView ==scrollSuperView)
{
if (scrollView.contentOffset.y>300) {
[scrollContentView setScrollEnabled:YES];
}else if (scrollView.contentOffset.y<10){
[scrollContentView setScrollEnabled:NO];
}
}
}
By the way scrollSuperView is outerScroll and scrollContentView is inner.
Any help appreciated.
1.scrollSuperView (Outer)
frame = CGRect(0, 0, 320, 468)
contentSize = CGSizeMake(320, 600)
2.scrollContentView (Inner)
frame = CGRect(0, 300, 320, 468)
contentSize = CGSizeMake(320, 600)
So i have above two scrollViews as mentioned outer and inner
Once outer scrollView reaches content offset of >300 then scrollEvent much be passed to inner ScrollView if user is scrolling by putting finger on inner ScrollView..
Hope its more clear now.
Setting outer scrollView's delaysContentTouches to NO will pass touch events from outer scrollView to inner scrollView... "in a single move"
-(void)scrollViewDidScroll:(UIScrollView *)scrollView
{
if (scrollView == scrollSuperView) {
//outer scrollView
if (scrollView.contentOffset.y >= 300) {
//NSLog(#"Outer scrollView ContentOffset has reached 300");
[scrollSuperView setDelaysContentTouches:NO];
[scrollContentView setScrollEnabled:YES];
}
}
else if (scrollView == scrollContentView) {
//inner scrollView
if (scrollView.contentOffset.y == 0) {
//NSLog(#"Inner scrollView ContentOffset has reached 0");
[scrollSuperView setDelaysContentTouches:YES];
[scrollContentView setScrollEnabled:NO];
}
}
}
Assumptions:
scrollSuperView (Outer)
frame = CGRect(0, 0, 320, 300)
contentSize = CGSizeMake(320, 600)
delaysContentTouches = YES
scrollEnabled = YES
scrollContentView (Inner)
frame = CGRect(0, 400, 320, 200)
contentSize = CGSizeMake(320, 400)
delaysContentTouches = YES
scrollEnabled = NO
-(void)viewDidLoad{
//write your code here
innerScrollView.scrollEnabled =NO;
outerScrollView.scrollEnabled =YES;
//above two lines makes touch(scroll) events given to outer and not inner.
}
And your modified method,
-(void)scrollViewDidScroll:(UIScrollView *)scrollView{
[self.view endEditing:YES];
if (scrollView ==scrollSuperView)
{
if (scrollView.contentOffset.y>300) {
outerScrollView.scrollEnabled =NO;
innerScrollView.scrollEnabled =YES;
[outerScrollView setContentOffset:CGPointMake(0, 200)];
}
}
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate{
[self.view endEditing:YES];
if (scrollView ==scrollSuperView && decelerate==NO)
{
if (scrollView.contentOffset.y>300) {
outerScrollView.scrollEnabled =NO;
innerScrollView.scrollEnabled =YES;
[outerScrollView setContentOffset:CGPointMake(0, 200) animated:YES];
}
}
}

iOS hiding navigation bar due to scroll amount

I've tried to implement such a table view that detects scroll amount and decides to show navigation bar or do not.
#interface HomeViewController () {
NSInteger scrollAmount;
bool navbarHidden = NO;
}
#implementation HomeViewController
#synthesize lastContentOffset = _lastContentOffset;
bool navbarHidden = NO;
- (void)awakeFromNib
{
[super awakeFromNib];
scrollAmount = 0;
distance = 50;
}
-(void)scrollViewDidScroll:(UIScrollView *)scrollView {
CGPoint offset = scrollView.contentOffset;
CGRect bounds = scrollView.bounds;
UIEdgeInsets inset = scrollView.contentInset;
if (offset.y > self.lastContentOffset.y)
{
scrollAmount++;
}
else
{
scrollAmount--;
}
bool awayFromTop = offset.y > distance + inset.top;
if (awayFromTop && !navbarHidden) {
[[self navigationController] setNavigationBarHidden:YES animated:YES];
navbarHidden = YES;
} else if (!awayFromTop || (scrollAmount < -100)) {
[[self navigationController] setNavigationBarHidden:NO animated:YES];
navbarHidden = NO;
}
self.lastContentOffset = offset;
}
-(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
scrollAmount = 0;
}
Basically, scrollViewDidScroll counts scroll amount and if user scrolls upward, it is decrementing scrollAmount by minus 1.
And if offset is close enough to top of the screen (!awayFromTop) OR scroll amount is smaller than -100, it is expected to navigation bar is hid.
When I put a NSLog for scrollAmount program runs correct, it is hiding nav. bar when user aways from top or shows when approaches to top and scrollAmount is printed correctly.
But whenever scrollAmount reaches to -100 [[self navigationController] setNavigationBarHidden:NO animated:YES]; is not executed and somehow scrollViewDidScroll is called infinitely I mean program enters an infinite loop. scrollAmount is printed like -100,-101,-102...,-1005...
Then I've used below code:
if ([scrollView.panGestureRecognizer translationInView:self.view].y < heightOfScreen/-4.0f && !navbarHidden) {
[self.navigationController setNavigationBarHidden:YES animated:YES];
navbarHidden = YES;
} else if ([scrollView.panGestureRecognizer translationInView:self.view].y > heightOfScreen/4.0f && navbarHidden) {
[self.navigationController setNavigationBarHidden:NO animated:YES];
navbarHidden = NO;
}
Obviously [scrollView.panGestureRecognizer translationInView:self.view].y gives sth. similar to scrollAmount but it works perfect, now I wonder why my implementation has been failed. Any ideas appreciated.
The code looks right but if I were you, I would use
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset
to track the scroll view scrolling, because scrollViewDidScroll generates some weird behavior in my app too. Maybe you'd like to give a try.

Several Questions about ScrollView with PageControl

I am pretty new to iOS development and I stumbled upon several issues for which I couldn't easily find any answers yet:
General Setup: I'm using a ScrollView with PageControl inside a TabBarApplication
Is it possible to have the PageControl within the same area as the content of the pages? For me it always gets hidden by the SrollView's Views, but due to display space being rare I really need it on the same height as the actual content.
I've fooled around in some Sandbox-Project and whenever I first started to implement a button into the View of a ScrollView-Page the Pages of the ScrollView wouldn't show immediately anymore, but only after the first scroll attempt. I'd post some code about that but its basically only autogenerated from IB.
This is a general Question about possibilities again: The main design of the Project should be a TabBarApplication with a NavigationController letting you go deeper into sub-menues like it is pretty common. Now in one of the Tabs there should be the PageControl, in which you can then again go into sub-menues by pushing Views on a NavigationController stack . Is this possible?
Some Code for 2.
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
NSMutableArray *controllers = [[NSMutableArray alloc] init];
for (unsigned i = 0; i < kNumberOfPages; i++) {
[controllers addObject:[NSNull null]]; // [TaskPageViewController new]];
}
self.viewControllers = controllers;
[controllers release];
// a page is the width of the scroll view
scrollView.pagingEnabled = YES;
scrollView.contentSize = CGSizeMake(scrollView.frame.size.width * kNumberOfPages, scrollView.frame.size.height);
scrollView.showsHorizontalScrollIndicator = NO;
scrollView.showsVerticalScrollIndicator = NO;
scrollView.scrollsToTop = NO;
scrollView.delegate = self;
pageControl.numberOfPages = kNumberOfPages;
pageControl.currentPage = 0;
}
- (IBAction)changePage:(id)sender {
int page = pageControl.currentPage;
// load the visible page and the page on either side of it (to avoid flashes when the user starts scrolling)
[self loadScrollViewWithPage:page - 1];
[self loadScrollViewWithPage:page];
[self loadScrollViewWithPage:page + 1];
// update the scroll view to the appropriate page
CGRect frame = scrollView.frame;
frame.origin.x = frame.size.width * page;
frame.origin.y = 0;
[scrollView scrollRectToVisible:frame animated:YES];
// Set the boolean used when scrolls originate from the UIPageControl. See scrollViewDidScroll: above.
pageControlUsed = YES;
}
- (void)loadScrollViewWithPage:(int)page {
if (page < 0) return;
if (page >= kNumberOfPages) return;
// replace the placeholder if necessary
TaskPageViewController *controller = [viewControllers objectAtIndex:page];
if ((NSNull *)controller == [NSNull null]) {
controller = [[TaskPageViewController alloc] init]; //WithPageNumber:page];
[viewControllers replaceObjectAtIndex:page withObject:controller];
[controller release];
}
// add the controller's view to the scroll view
if (nil == controller.view.superview) {
CGRect frame = scrollView.frame;
frame.origin.x = frame.size.width * page;
frame.origin.y = 0;
controller.view.frame = frame;
[scrollView addSubview:controller.view];
}
}
- (void)scrollViewDidScroll:(UIScrollView *)sender {
// We don't want a "feedback loop" between the UIPageControl and the scroll delegate in
// which a scroll event generated from the user hitting the page control triggers updates from
// the delegate method. We use a boolean to disable the delegate logic when the page control is used.
if (pageControlUsed) {
// do nothing - the scroll was initiated from the page control, not the user dragging
return;
}
// Switch the indicator when more than 50% of the previous/next page is visible
CGFloat pageWidth = scrollView.frame.size.width;
int page = floor((scrollView.contentOffset.x - pageWidth / 2) / pageWidth) + 1;
pageControl.currentPage = page;
// load the visible page and the page on either side of it (to avoid flashes when the user starts scrolling)
[self loadScrollViewWithPage:page - 1];
[self loadScrollViewWithPage:page];
[self loadScrollViewWithPage:page + 1];
// A possible optimization would be to unload the views+controllers which are no longer visible
}
You can have two view hierarchies for this:
Have the page control inside scrollview with origin fixed at contentOffset property
Have the page control in the superview of scrollView, but at a higher index (i.e. floating above it)
This depends on where you put the code of adding the subviews. Is it in the delegate method of scrollView? viewDidLoad? Somewhere else? Some code might help.
Not sure why you'd need to have a page control when it's a drill-down navigation. Pages are for navigating same level items.

Resources