Customizing UIView class with UIScrollview and UITableview - ios

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

Related

Avoid UIPageViewController "over-scrolling"

I have 2 VCs on my UIPageViewController.
I want that if the user is on the 1st view, he won't be able to swipe to the right (just to the left - to the 2nd view), and if he's on the 2nd view he won't be able to swipe to the left (just to the right - to the 1st view).
I want that the user won't be able to scroll - not a black view (what happens when I return nil on: - (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController)
The "black view" is likely to be the background of your main view, or the background colour of your UIPageViewController.
You can kill scrolling in one of the directions depending on the ViewController that the PageViewController is currently showing
first set your VC as the delegate for the PageViewController's inner scroll view. You can add a more sophisticated subviews traversal here, this is a simple version:
for (UIView *view in self.pageViewController.view.subviews) {
if ([view isKindOfClass:[UIScrollView class]]) {
[(UIScrollView *)view setDelegate:self];
}
}
then add a property to your VC as this:
#property (nonatomic) CGPoint lastOffset;
and after that implement the following scroll view delegate methods
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
scrollView.scrollEnabled = YES;
}
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
scrollView.scrollEnabled = YES;
self.lastOffset = scrollView.contentOffset;
}
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
CGPoint nowOffset = scrollView.contentOffset;
NSLog(#"delta %f", self.lastOffset.x - nowOffset.x);
if ((self.lastOffset.x - nowOffset.x) < 0) {
//uncomment to prevent scroll to left
//scrollView.scrollEnabled = NO;
} else if ((self.lastOffset.x - nowOffset.x) > 0) {
//uncomment to prevent scroll to right
//scrollView.scrollEnabled = NO;
} else {
scrollView.scrollEnabled = YES;
}
}

how to disable scrollover top in uitableview , ios

I'm new to iOS. I want to disable scrollovertop in UITableView. I did try [self.table setBounce:NO], but my table's scrolloverbottom was disabled as well. I just want to disable scrollovertop. Please help me out.
Try this
-(void)scrollViewDidScroll:(UIScrollView *)scrollView
{
CGPoint point = [scrollView contentOffset];
if (point.y < 0)
{
[scrollView setContentOffset:CGPointMake(0, 0)];
}
}
Don't forget to add UITableViewDelegate

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];
}
}
}

UIPanGestureRecognizer conflict with scrollview

I'm trying to add a pan gesture recognizer to a view containing a scrollview, but I guess I've problems with priorities.
My global UIView has a UIPanGestureRecognizer set like this:
_bottomPanGestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(bottomPanGestureDetected:)];
_bottomPanGestureRecognizer.minimumNumberOfTouches = 2;
_bottomPanGestureRecognizer.maximumNumberOfTouches = 2;
_bottomPanGestureRecognizer.delaysTouchesBegan = NO;
_bottomPanGestureRecognizer.delaysTouchesEnded = NO;
I want to recognize this gesture to display another view from the bottom with some sort of pinch down-to-up.
The problem is that the scrollview is recognizing its own pan gesture before mine.
So I tried to delay it thanks to:
[_scrollView.panGestureRecognizer requireGestureRecognizerToFail:_bottomPanGestureRecognizer];
And it's working, the scrollview event is fired after my two finger down to up recognizer, but the problem is now when I only use one finger to scroll in the scrollview, the scroll works after a small delay.
I would like to have no delay for this event, is this possible? Any idea welcomed!
Cheers.
Cyril
In case it isn't solved yet, i solved the problem for me.
I added a UIPanGestureRecognizer to a UIScrollView to detect two finger pan gestures and the default UIScrollView behaviour (scrolling to something) still workes.
So what i did is to add the UIPanGestureReconizer to the UIScrollView:
UIPanGestureRecognizer *pangestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(displayReloadIndicator:)];
pangestureRecognizer.minimumNumberOfTouches = 2;
pangestureRecognizer.delegate = self;
[self.scrollView addGestureRecognizer:pangestureRecognizer];
[pangestureRecognizer release];
After this i added the code:
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
return YES;
}
After this, i implemented the pan gesture recognizers action method.
- (void) displayReloadIndicator:(UIPanGestureRecognizer*) panGestureRecognizer {
UIGestureRecognizerState gestureRecognizerState = gestureRecognizer.state;
CGPoint translation = [gestureRecognizer translationInView:self.scv_bibgesamt];
if (gestureRecognizerState == UIGestureRecognizerStateBegan) {
// create a UIView with all the Pull Refresh Headers and add to UIScrollView
// This is really much lines of code, but its simply creating a UIView (later you'll find a myRefreshHeaderView, which is my base view) and add UIElements e.g. UIActivityIndicatorView, a UILabel and a UIImageView on it
// In iOS 6 you will also have the possibility to add a UIRefreshControl to your UIScrollView
}
else if (gestureRecognizerState == UIGestureRecognizerStateEnded
|| gestureRecognizerState == UIGestureRecognizerStateCancelled) {
if (translation.y >= _myRefreshHeaderView.frame.size.height + 12) { // _myRefreshHeaderView is my baseview
//so the UIScrollView has been dragged down with two fingers over a specific point and have been release now, so we can refresh the content on the UIScrollView
[self refreshContent];
//animatly display the refresh view as the top content of the UIScrollView
[self.scrollView setContentOffset:CGPointMake(0, myRefreshHeaderView.frame.size.height) animated:YES];
}
else {
//the UIScrollView has not been dragged over a specific point so don't do anything (just scroll back to origin)
[self.scrollView setContentOffset:CGPointMake(0, 0) animated:YES];
//remove the view (because it's no longer needed)
[_myRefreshHeaderView removeFromSuperview];
}
}
UPDATE:
In case you may wish to integrate the swipe back functionality from your navigationcontroller, you should integrate following code:
- (void) viewDidLoad {
[super viewDidLoad];
if ([self.navigationController respondsToSelector:#selector(interactivePopGestureRecognizer)]) {
self.navigationController.interactivePopGestureRecognizer.enabled = YES;
self.navigationController.interactivePopGestureRecognizer.delegate = nil;
}
//setup view controller
}
and
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
if (gestureRecognizer == _panGestureRecognizer
&& [self.navigationController.viewControllers count] > 1) {
CGPoint point = [touch locationInView:self.view.window];
if (point.x < 20
|| point.x > self.view.window.frame.size.width - 20) {
return NO;
}
}
return YES;
}
Implement panRecognizer delegate to enable simultaneously recognize UIScrollView UIGestureRecognizer
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
if (_panRecognizer == gestureRecognizer) {
if ([otherGestureRecognizer.view isKindOfClass:UIScrollView.class]) {
UIScrollView *scrollView = (UIScrollView *)otherGestureRecognizer.view;
if (scrollView.contentOffset.x == 0) {
return YES;
}
}
}
return NO;
}
SWIFT 4
If using a scrollView you can use below, it is only a panGestureRecognizer when a scrollView is dragging at the very top:
translation.y > 0 means you are moving from top to bottom and
locationInScrollView.y < 500.0 means you end dragging at 500 or less (you can customize this) to prevent that the refresh is done in middle or bottom of the scroll.
func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
let translation = scrollView.panGestureRecognizer.translation(in: scrollView.superview)
let locationInScrollView = scrollView.panGestureRecognizer.location(in: scrollView)
if translation.y > 0 && locationInScrollView.y < 500.0 {
print("scrollView refresh: Y: \(locationInScrollView.y)")
setupUI()
}
}

How to implement the scroll process of tableheaderview?

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];
}
}
}
}

Resources