How to know in which way UITableView is being scrolled - iphone

Is there any way in which we can know if a UITableView is being scrolled in upward direction or downward direction?

-(void) scrollViewDidScroll:(UIScrollView *)scrollView
{
CGPoint currentOffset = scrollView.contentOffset;
if (currentOffset.y > self.lastContentOffset.y)
{
// Downward
}
else
{
// Upward
}
self.lastContentOffset = currentOffset;
}

-(void)scrollViewWillEndDragging:(UIScrollView *)scrollView
withVelocity:(CGPoint)velocity
targetContentOffset:(inout CGPoint *)targetContentOffset{
if (velocity.y > 0){
NSLog(#"up");
}
if (velocity.y < 0){
NSLog(#"down");
}
}

Could we do like this?
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
if ([scrollView.panGestureRecognizer translationInView:scrollView].y > 0) {
// down
} else {
// up
}
}

UITableView is a UIScrollView subclass, thus you can set yourself as the UIScrollViewDelegate and get scroll view delegate callbacks.
The argument for one of these delegate methods (-scrollViewDidScroll:) is the scroll view that did scroll, you can compare it to your table views to know which one it was that scrolled.
Sorry, I misread your question. I thought you wanted to know which table view is being scrolled (I missed the "way").
To know the direction you can keep the previous offset in a variable and see if the delta (current.y - previous.y) is positive (scrolling down) or negative (scrolling up).

You can track the difference in content offset. Keep the old one in a member/static variable and check against the current. If the old value it's lower then the scrolling was directed downwards and vice versa.

override func scrollViewWillEndDragging(scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
if targetContentOffset.memory.y < scrollView.contentOffset.y {
//println("Going up!")
} else {
// println("Going down!")
}
}

You can do this by implementing UIScrollView's delegate method in this way, it's graceful.
PS: lastOffset and scrollingUpward is property of ViewController.
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
CGPoint currentOffset = scrollView.contentOffset;
self.scrollingUpward = currentOffset.y > self.lastOffset.y;
self.lastOffset = currentOffset;
}

- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
{
if (yourTableView.isDragging || yourTableView.isDecelerating)
{
// your tableview is scrolled.
// Add your code here
}
}
Here you have to replace your tableview name instead of "yourTableView".
yourTableView.isDragging - It returns YES if user has started scrolling. this may require some time and or distance to move to initiate.
yourTableView.isDecelerating - It returns YES if user isn't dragging (touch up) but scroll view is still moving.

Related

How would I go about disabling a UIButton if the UIScrollView has scrolled more than a certain amount?

How would I go about disabling a UIButton if the UIScrollView has scrolled more than a certain amount?
this is what I've been trying. Perhaps it's the wrong scrollViewDidScroll: delegate method.
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
if (_scrollView.contentOffset.y >= 100) {
mapLaunchButton.enabled = NO;
}
}
thanks for any help
Simple! You'll need to create a variable to store the starting position of the scroll view though. It should be a CGPoint. Set it to the scroll view's content offset in scrollViewWillBeginDragging: (where the scroll view starts moving) and then do comparison in scrollViewDidScroll similarly to how you were doing it before.
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
{
startingPoint = scrollView.contentOffset;
}
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
if (scrollView.contentOffset.y >= startingPoint.y + 100.0f) {
mapLaunchButton.enabled = NO;
}
}
Keep in mind you may need to modify the values I've provided slightly depending on the starting position of the scroll view, and the direction in which you'd like to monitor the changes.
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
if (_scrollView.contentOffset.y >= 100) {
mapLaunchButton.enabled = NO;
}
else {
mapLaunchButton.enabled = YES;
}
}
The code is OK, but you have to add the delegate for the scrollView
- (void)viewDidLoad {
[super viewDidLoad];
// do whatever
...
// Add the delegate for the scrollview
[_scrollView setDelegate:self];
}

Action while scroll up from UITableview

I want to write an action when user scroll up from UITableView,How can I do this ?
Try this
Step 1:
yourUITableView.delegate = self;
Step 2:
CGFloat yOffset = 0.0;
Step 3:
-(void)scrollViewDidScroll:(UIScrollView *)scrollView
{
if (scrollView.contentOffset.y < yOffset) {
// scrolls down.
yOffset = scrollView.contentOffset.y;
}
else
{
// scrolls up.
yOffset = scrollView.contentOffset.y;
// Your Action goes here...
}
}
There are several ways I can think of that can help you with this. For once, if you don't need to have a precise recognition of the upward swiping you can use the UITableView behaviour to do this.
Each time a new row appears, tableView:cellForRowAtIndexPath: selector is called. You can use this method to know de direction the user is scrolling by comparing the previously inserted row (indexPath.row) to see if the newer is lower. If it is, then the user is scrolling up.
For more precision you can try using the Swipe Gesture Recognizer. I've personally never used it, but I can't imagine it being hard to use.
UITableView inherits from UIScrollView, so you can use the delegate from UIScrollView for your tableView: http://developer.apple.com/library/ios/#documentation/uikit/reference/UIScrollViewDelegate_Protocol/Reference/UIScrollViewDelegate.html#//apple_ref/occ/intf/UIScrollViewDelegate
tableView.delegate = self;
..
- (void)scrollViewWillBeginDecelerating:(UIScrollView *)scrollView
{
NSLog(#"Content offset: %f", scrollView.contentOffset.y);
// do something like
// if firstOffset < secondOffset {
// [self yourAction];
//}
}

UIScrollView did Scroll

I want to show an image that stays on the page for 5 seconds but appears everytime my scrollview scrolls. So obviously i need to marry animation for the UILabel and some method of UIScrollView. Im not sure which one to use to be honest. Also i have two UIScrollViews on one UIViewController so i dont know what i should set as delegate.
The following is the animation i have at the moment
[UIView animateWithDuration:0.3 animations:^{ // animate the following:
pageCountImage.frame = CGRectMake(0, 0, 100, 50); // move to new location
}];
Your view controller can be the delegate of both scroll views. Agree with #Ravi that you can use the delegate param to determine which scroll view is scrolling.
Sounds like you need a few animations packaged to make sense for the UI:
// hide or show the page count image after a given delay, invoke completion when done
- (void)setPageCountImageHidden:(BOOL)hidden delay:(NSTimeInterval)delay completion:(void (^)(BOOL))completion {
BOOL currentlyHidden = self.pageCountImage.alpha == 0.0;
if (hidden == currentlyHidden) return;
[UIView animateWithDuration:0.3 delay:delay options:UIViewAnimationOptionBeginFromCurrentState animations:^{
self.pageCountImage.alpha = (hidden)? 0.0 : 1.0;
} completion:completion];
}
// move the page count image to the correct position given a scroll view content offset
- (void)positionPageControlForContentOffset:(CGFloat)xOffset {
// assume page width is a constant (the width of a page in the scroll view)
NSInteger page = xOffset / kPAGEWIDTH;
// assume max page is a constant (the max number of pages in scroll view)
// scroll positions in the "bounce" will generate page numbers out of bounds, fix that here...
page = MAX(MIN(page, kMAXPAGE), 0);
// kPAGE_INDICATOR_WIDTH the distance the page image moves between pages
// kPAGE_INDICATOR_ORIGIN the page image x position at page zero
CGFloat xPosition = kPAGE_INDICATOR_ORIGIN + page * kPAGE_INDICATOR_WIDTH;
// assume y position and size are constants
CGRect pageIndicatorFrame = CGRectMake(xPosition, kYPOS, kWIDTH, kHEIGHT);
// finally, do the animation
[UIView animateWithDuration:0.3 animations:^{
self.pageCountImage.frame = pageIndicatorFrame;
}];
}
Then in view did scroll:
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
if (scrollView == /* the scroller with the page control */) {
[self setPageCountImageHidden:NO delay:0.0 completion:^(BOOL finished) {
[self positionPageControlForContentOffset:scrollView.contentOffset.x];
[self setPageCountImageHidden:YES delay:5.0 completion:^(BOOL finished){}];
}];
}
// and so on...
You should implement <UIScrollViewDelegate>. Make use of the method - (void)scrollViewDidScroll:(UIScrollView *)scrollView and write your animation code in there. If you have multiple scroll views, you could do this:
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
if(scrollView == myScrollView1)
// do something
else if (scrollView == myScrollView2)
// do something else
else
// do something else
}
just like tableview you should configure and control your scroll actions. You can write an extension of your controller and control all scrolling actions here. As an example you can check:
extension ExampleViewController: UIScrollViewDelegate {
func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
// your actions here
}
func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView) {
// your actions here
}
func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
// your actions here
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
// your actions here
}
}

Limiting the scrollable area in UIScrollView

I have a UIScrollView that is scrolling a fairly large UIView.
At certain times I want to limit the area the user can scroll around in. For example, I may only want to allow them to view the bottom quarter of the view.
I am able to limit the area by overriding scrollViewDidScroll and then calling setContentOffset if the view has scrolled too far. But this way I can't get it bounce back as smoothly as the UIScrollView can naturally do when scrolling beyond the bounds of the UIView.
Is there a better way to limit the scrollable area in a UIScrollView?
I would change the contentSize property of the scroll view to the size of the area you want the user to be able to scroll around in and adjust the frame.origin of the subview such the upper left boundary you want appears at (0, 0) relative to the scroll view. For example, if your view is 800 points tall and you want to show the bottom quarter, set the height of contentSize to 200 and set the y component of view.frame.origin to -600.
I've found something that works for me. It let's you scroll to point 0,0 but no further:
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
if (scrollView.contentOffset.x <= -1) {
[scrollView setScrollEnabled:NO];
[self.scrollView setContentOffset:CGPointMake(0, 0) animated:YES];
[scrollView setScrollEnabled:YES];
}
}
You could do the same for top, bottom or right (x or y)
a small improvement on Yoko's answer in Swift 4 will be
override func scrollViewWillBeginDecelerating(_ scrollView: UIScrollView) {
if scrollView.contentOffset.y > 600 {
let anim = UIViewPropertyAnimator(duration: 1, dampingRatio: 0.5) {
scrollView.isScrollEnabled = false
scrollView.setContentOffset(CGPoint(x: 0, y: 600), animated: false)
scrollView.isScrollEnabled = true
}
anim.startAnimation()
}
}
which will make the scrollview animate really similar to what its supposed to do. The slower drag when you are in the "bounce" area will not work and animation duration has to depend on the distance (not constant like here) if you want to be exact. You can also try to do this logic in scrollViewDidScroll and see how it differs. The key thing is that setContentOffset(_:,animated:) has to be with animated: false so that the UIViewPropertyAnimator's block can capture it and animate it
Another approach is to override the UIScrollView's method:
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event.
Returning YES will allow the user to scroll. Returning NO will not.
NOTE: This will disable all touches to any views imbedded inside the UIScrollView that pointInside returns NO to. Useful if the area you don't want to scroll from doesn't have any interaction.
This example only allows the UIScrollView to scroll when the user is scrolling over a UITableView. (A UITableView and two UIViews are imbedded inside the UIScrollView)
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
for (UIView *subview in self.subviews) {
if ([subview pointInside:[self convertPoint:point toView:subview] withEvent:event] && ![subview isKindOfClass:[UITableView class]]) {
return NO;
}
}
return YES;
}

UIScrollView scroll detection

I have a UIScrollView in a UIViewController view that scrolls horizontally. How can I detect whether the scroll is at the left end or right end or somewhere in the middle?
You will probably need to look in to a scrollViewDelegate method such as below.
ObjC
- (void)scrollViewDidScroll:(UIScrollView *)scrollView{
NSLog(#"Point: %#", NSStringFromCGPoint(scrollView.contentOffset));
}
Swift
override func scrollViewDidScroll(scrollView: UIScrollView) {
println(scrollView.contentOffset)
}
Have a look at the apple docs.
Also make sure to set your scrollview delegate your_scroll_view.delegate = self; and your view controller must conform to <UIScrollViewDelegate>
GameBit is correct here, but to elaborate -
The UIScrollView has a member variable contentOffset, that describes how many pixels from the origin the scrollview has scrolled. A positive value is a scroll to the right, negative is a scroll to the left.
Is your UIScrollView in Paged mode? if so this will help:
CGFloat pageWidth = scrollView.frame.size.width;
int page = floor((scrollView.contentOffset.x - pageWidth / 2) / pageWidth) + 1;
This works for me in horizontal UIScrollView
Conform to UIScrollViewDelegate
In your ViewController - yourScrollView.delegate = self
func scrollViewDidEndDecelerating(scrollView: UIScrollView) {
var currentPage = yourScrollView.contentOffset.x / yourScrollView.bounds.size.width;
}
Do not use scrollViewDidScroll as you need to wait until scrolling ends