This question already has answers here:
Cancel UIScrollView bounce after dragging
(2 answers)
Closed 7 years ago.
How to cancel UIScrollView bounce animation after dragging? ( NOT DISABLE BOUNCE )
Thanks!
some kind effect like dragRefresh? If it is, you should set your scrollView contentInset at scrollView end drag delegate method.
set the content offset in the scrollView delegate method scrollViewWillEndDragging:withVelocity:targetContentOffset:
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset;
{
scrollView.contentOffset = //.. content offset of where you want it to stop, use the current offset if you want it to stay where it is.
}
Your question is vague and doesn't give me much to go on to provide a better answer, hope this is of help.
[scrollView setContentOffset:scrollView.contentOffset animated:NO];
NOTE: You must use the animated version of the function - not the version without the animated flag.
Related
A user performs a quick swipe gesture to make a UICollectionView start scrolling (it will gradually come to an halt).
How can I programmatically force the scrolling to come to an immediate stop?
To clarify, I want to allow the deceleration but I need to be able to stop it in code.
Have you tried the following?
[self.collectionView setContentOffset:self.collectionView.contentOffset animated:NO];
the contentOffset property is constantly updated as the collectionView scrolls (even via animation) so at the time of calling the above, it should hopefully force the collectionView to stop its existing animation.
Try this one. Worked for me. :)
self.collectionView.scrollEnabled = NO;
For Swift 3:
collectionView.isScrollEnabled = false
if you have the pagingEnabled and scrollEnabled properties set to true than this should work:
self.collectionView.scrollEnabled = false
self.collectionView.pagingEnabled = false
Adopt the following scrollViewDelegate method to pick up when the user lets go of dragging the collectionView.
-(void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset;
You can then just create your own animation block to set whichever speed/final destination you think looks best using the contentOffset property.
According to the official UIScrollView documentation related to scrollsToTop:
If that scroll view has scrollsToTop set to NO, its delegate returns
NO from scrollViewShouldScrollToTop:, or the content is already at
the top, nothing happens.
So, as a result, the delegate method scrollViewShouldScrollToTop: is not fired when the scrollview is at the top when I tap the status bar. However, I'm trying to take advantage of this call to programatically make my own decision about which scrollview in the hierarchy needs to scroll.
So what is the best alternative to this? I'm trying to find a way to catch taps on the status bar more than anything. Based on what I've read it sounded like this was the best way to catch the call and handle it appropriately.
EDIT: The next best alternative I could think of was to place a clear UIView with a UITapGestureRecognizer over top of the status bar via a different UIWindow that sits on top.
Since iOS7 controller views go by default underneath statusBar and navigationBars.
You can take advantage of this, and add a TapGestureRecognizer to your VC´s main view. In the tap delegate method check whether the tapped point in view belongs to the status bar frame.
- (void)userTappedOnView:(UITapGestureRecognizer *)recognizer {
if (recognizer.state == UIGestureRecognizerStateEnded) {
CGPoint tapLocation = [recognizer locationInView:self.view];
CGRect statusBarFrame = [[UIApplication sharedApplication] statusBarFrame];
if (CGRectContainsPoint(statusBarFrame, point)) {
//Scroll other views to top here..
}
}
}
Here's how the scroll views work: One scroll view is paging enabled in the horizontal direction. Each 'page' of this scroll view contains a vertically scrolling UITableView. Without modification, this works OK, but not perfectly.
The behaviour that's not right: When the user scrolls up and down on the table view, but then wants to flick over to the next page quickly, the horizontal flick/swipe will not work initially - it will not work until the table view is stationary (even if the swipe is very clearly horizontal).
How it should work: If the swipe is clearly horizontal, I'd like the page to change even if the table view is still scrolling/bouncing, as this is what the user will expect too.
How can I change this behaviour - what's the easiest or best way?
NOTE For various reasons, a UIPageViewController as stated in some answers will not work. How can I do this with cross directional UIScrollViews (/one is a table view, but you get the idea)? I've been banging my head against a wall for hours - if you think you can do this then I'll more than happily award a bounty.
According to my understanding of the question, it is only while the tableView is scrolling we want to change the default behaviour. All the other behaviour will be the same.
SubClass UITableView. UITableViews are subClass of UIScrollViews. On the UITableView subClass implement one UIScrollView's UIGestureRecognizer's delegate method
- (BOOL)gestureRecognizer:(UIPanGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UISwipeGestureRecognizer *)otherGestureRecognizer
{
//Edit 1
//return self.isDecelerating;
//return self.isDecelerating | self.bounces; //If we want to simultaneous gesture on bounce and scrolling
//Edit 2
return self.isDecelerating || self.contentOffset.y < 0 || self.contentOffset.y > MAX(0, self.contentSize.height - self.bounds.size.height); // #Jordan edited - we don't need to always enable simultaneous gesture for bounce enabled tableViews
}
As we only want to change the default gesture behaviour while the tableView is decelerating.
Now change all 'UITableView's class to your newly created tableViewSubClass and run the project, swipe should work while tableView is scrolling. :]
But the swipe looks a little too sensitive while tableView is scrolling. Let's make the swipe a little restrictive.
SubClass UIScrollView. On the UIScrollView subclass implement another UIGestureRecognizer's delegate method gestureRecognizerShouldBegin:
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
if ([gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]]) {
CGPoint velocity = [(UIPanGestureRecognizer *)gestureRecognizer velocityInView:self];
if (abs(velocity.y) * 2 < abs(velocity.x)) {
return YES;
}
}
return NO;
}
We want to make the "swipe is clearly horizontal". Above code only permits gesture begin if the gesture velocity on x axis is double than on y axis. [Feel free to increase the hard coded value "2" if your like. The higher the value the swipe needs to be more horizontal.]
Now change the `UiScrollView' class (which has multiple TableViews) to your ScrollViewSubClass. Run the project. :]
I've made a project on gitHub https://github.com/rishi420/SwipeWhileScroll
Although apple doesn't like this method too much:
Important: You should not embed UIWebView or UITableView objects in UIScrollView objects. If you do so, unexpected behavior can result
because touch events for the two objects can be mixed up and wrongly
handled.
I've found a great way to accomplish this.
This is a complete solution for the problem. In order to scroll the UIScrollView while your UITableView is scrolling you'll need to disable the interaction you have it.
- (void)viewDidLoad
{
[super viewDidLoad];
_myScrollView.contentSize = CGSizeMake(2000, 0);
data = [[NSMutableArray alloc]init];
for(int i=0;i<30;i++)
{
[data addObject:[NSString stringWithFormat:#"%d",i]];
}
UITapGestureRecognizer * tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleTap:)];
[self.view addGestureRecognizer:tap];
}
- (void)handleTap:(UITapGestureRecognizer *)recognizer
{
[_myTableView setContentOffset:_myTableView.contentOffset animated:NO];
}
- (void)scrollViewWillBeginDecelerating:(UIScrollView *)scrollView
{
scrollView.userInteractionEnabled = NO;
}
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
scrollView.userInteractionEnabled = YES;
}
To sum up the code above, if the UITableView is scrolling, set userInteractionEnabled to NO so the UIScrollView will detect the swipe. If the UITableView is scrolling and the user taps on the screen, userInteractionEnabled will be set to YES.
Instead of using UIScrollView as a container for these multiple table views, try using a UIPageViewController.
You can even integrate this into your existing view controller setup as a child view controller (directly replacing the UIScrollView).
In addition, you'll likely want to implement the required methods from UIPageViewControllerDataSource and possibly one or more of the methods from UIPageViewControllerDelegate.
Did you try the methods : directionalLockEnabled of both your table and scroll and set them up to horizontal for one and vertical for the other ?
Edit :
1)
What you want to do is very complicate since the touch wait some time (like 0.1s) to know what your movement will be. And if your table is moving, it will take your touch immediately whatever it is (because it's suppose to be reactive movement on it).
I don't see any other solution for you but to override touch movement from scratch to detect immediately the kind of mouvement you want (like if the movement will be horizontal) but it will be more than hard to do it good.
2)
Another solution I can advise you is to make your table have left and right margin, where you can touch the parent scroll (pages thing so) and then even if your table is scrolling, if you touch here, only your paging scroll will be touched. It's simpler, but could not fit with your design maybe...
Use UIPageViewController and in the -viewDidLoad method (or any other method what best suits your needs or design) get UIPageViewController's UIScrollView subview and assign a delegate to it. Keep in mind that, its delegate property won't be nil. So optionally, you can assign it to another reference, and then assign your object, which conforms to UIScrollViewDelegate, to it. For example:
id<UIScrollViewDelegate> originalPageScrollViewDelegate = ((UIScrollView *)[pageViewController.view.subviews objectAtIndex:0]).delegate;
[((UIScrollView *)[pageViewController.view.subviews objectAtIndex:0]) setDelegate:self];
So that you can implement UIScrollViewDelegate methods with ease. And your UIPageViewController will call your delegate's -scrollViewDidScroll: method.
By the way, you may be obliged to keep original delegate, and respond to delegate methods with that object. You can see an example implementation in ViewPagerController class on my UI control project here
I faced the same thing recently. My UIScrollview was on paging mode and every page contained a UITableView and like you described it worked but not as you'd expected it to work. This is how solved it.
First I disabled the scrolling of the UIScrollview
Then I added a UISwipeGestureRecognizer to the actual UITableView for left and right swipes.
The action for those swipes were:
[scroll setContentOffset:CGPointMake(currentPointX + 320, PointY) animated:YES];
//Or
[scroll setContentOffset:CGPointMake(currentPointX - 320 , PointY) animated:YES];
This works flawlessly, the only down side is that if the user drags his finger on the UITableVIew that will be considered as a swipe. He won't be able to see half of screen A and half of screen B on the same screen.
You could subclass your scroll view and your table views, and add this gesture recognizer delegate method to each of them...
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
shouldRecognizeSimultaneouslyWithGestureRecognizer:
(UIGestureRecognizer *)otherGestureRecognizer {
return YES;
}
I can't be sure this is exactly what you are after, but it may come close.
I have a Scrollview, it's properties are set in viewDidAppear.
Now when I get to the Scrollview first time there isn't any problem. However I have buttons that are assigned to UINavigationController. So when I press into one of them UINavigationController opens up, when I close the navigation controller, ScrollView does not restore properly. It basically aligns the centre of the screen as previously pressed button location. So if I try to scroll up it does not.
I have tried using this in my viewDidAppear:
scrollView.center = CGPointMake(scrollView.contentOffset.x, scrollView.contentOffset.y);
Which did not quite work. How can I solve this? I am using iOS6.1
Actually I found the answer here:
UIScrollview Autolayout Issue
The exact code that I used is:
- (void)viewDidDisappear:(BOOL)animated {
[super viewDidDisappear:animated];
//save the current offset
previousPoint = scrollView.contentOffset;
//set current view to the beginning point
self.scrollView.contentOffset = CGPointZero;
}
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
//retrieve the previous offset
self.scrollView.contentOffset = previousPoint;
}
previousPoint is nothing but a CGPoint variable declared on the implementation.
I've had this problem before too. This answer shows how to overcome this issue.
Basically, you need to set the scrollview's contentOffset appropriately in viewWillAppear: and viewDidDisappear:.
EDIT: Here's another related question that you might find useful, UIScrollview Autolayout Issue.
I have a UIScrollView that I'm scrolling using setContentOffset:animate:YES but if the device rotates I want to be able to stop it. Is this possible? Or how would I go about writing my own implementation of the setContentOffset with animation? thanks!
scrollview.contentOffset = scrollView.contentOffset should stop the scrollView at its last scroll position.
Do this in willAnimateRotationTo..... in your viewController
If you have a UIScrollView sub-class you could do it in setFrame: , this is assuming the frame of the scrollview changes upon orientation change.
try to use this instead:
[UIView animateWithDuration:.25 animations:^
{
// replacement for setContentOffset:animated:
self.scrollView.contentOffset = scrollPositionBeforeKeyboardAdjustments;
}];
if you are struggling with the keyboard, see my answer
How to make a UITextField move up when keyboard is present?