可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I have a UIScrollView
with only horizontal scrolling allowed, and I would like to know which direction (left, right) the user scrolls. What I did was to subclass the UIScrollView
and override the touchesMoved
method:
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
[super touchesMoved:touches withEvent:event];
UITouch *touch = [touches anyObject];
float now = [touch locationInView:self].x;
float before = [touch previousLocationInView:self].x;
NSLog(@\"%f %f\", before, now);
if (now > before){
right = NO;
NSLog(@\"LEFT\");
}
else{
right = YES;
NSLog(@\"RIGHT\");
}
}
But this method sometimes doesn\'t get called at all when I move. What do you think?
回答1:
Determining the direction is fairly straightforward, but keep in mind that the direction can change several times over the course of a gesture. For example, if you have a scroll view with paging turned on and the user swipes to go to the next page, the initial direction could be rightward, but if you have bounce turned on, it will briefly be going in no direction at all and then briefly be going leftward.
To determine the direction, you\'ll need to use the UIScrollView scrollViewDidScroll
delegate. In this sample, I created a variable named lastContentOffset
which I use to compare the current content offset with the previous one. If it\'s greater, then the scrollView is scrolling right. If it\'s less then the scrollView is scrolling left:
// somewhere in the private class extension
@property (nonatomic, assign) CGFloat lastContentOffset;
// somewhere in the class implementation
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
ScrollDirection scrollDirection;
if (self.lastContentOffset > scrollView.contentOffset.x) {
scrollDirection = ScrollDirectionRight;
} else if (self.lastContentOffset < scrollView.contentOffset.x) {
scrollDirection = ScrollDirectionLeft;
}
self.lastContentOffset = scrollView.contentOffset.x;
// do whatever you need to with scrollDirection here.
}
I\'m using the following enum to define direction. Setting the first value to ScrollDirectionNone has the added benefit of making that direction the default when initializing variables:
typedef NS_ENUM(NSInteger, ScrollDirection) {
ScrollDirectionNone,
ScrollDirectionRight,
ScrollDirectionLeft,
ScrollDirectionUp,
ScrollDirectionDown,
ScrollDirectionCrazy,
};
回答2:
...I would like to know which direction (left, right) the user scrolls.
In that case, on iOS 5 and above, use the UIScrollViewDelegate
to determine the direction of the user\'s pan gesture:
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
{
if ([scrollView.panGestureRecognizer translationInView:scrollView.superview].x > 0) {
// handle dragging to the right
} else {
// handle dragging to the left
}
}
回答3:
Using scrollViewDidScroll:
is a good way to find the current direction.
If you want to know the direction after the user has finished scrolling, use the following:
@property (nonatomic) CGFloat lastContentOffset;
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
self.lastContentOffset = scrollView.contentOffset.x;
}
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
if (self.lastContentOffset < scrollView.contentOffset.x) {
// moved right
} else if (self.lastContentOffset > scrollView.contentOffset.x) {
// moved left
} else {
// didn\'t move
}
}
回答4:
No need to add an extra variable to keep track of this. Just use the UIScrollView
\'s panGestureRecognizer
property like this. Unfortunately, this works only if the velocity isn\'t 0:
CGFloat yVelocity = [scrollView.panGestureRecognizer velocityInView:scrollView].y;
if (yVelocity < 0) {
NSLog(@\"Up\");
} else if (yVelocity > 0) {
NSLog(@\"Down\");
} else {
NSLog(@\"Can\'t determine direction as velocity is 0\");
}
You can use a combination of x and y components to detect up, down, left and right.
回答5:
The solution
func scrollViewDidScroll(scrollView: UIScrollView) {
if(scrollView.panGestureRecognizer.translationInView(scrollView.superview).y > 0)
{
print(\"up\")
}
else
{
print(\"down\")
}
}
回答6:
In iOS8 Swift I used this method:
override func scrollViewDidScroll(scrollView: UIScrollView){
var frame: CGRect = self.photoButton.frame
var currentLocation = scrollView.contentOffset.y
if frame.origin.y > currentLocation{
println(\"Going up!\")
}else if frame.origin.y < currentLocation{
println(\"Going down!\")
}
frame.origin.y = scrollView.contentOffset.y + scrollHeight
photoButton.frame = frame
view.bringSubviewToFront(photoButton)
}
I have a dynamic view which changes locations as the user scrolls so the view can seem like it stayed in the same place on the screen. I am also tracking when user is going up or down.
Here is also an alternative way:
func scrollViewWillEndDragging(scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
if targetContentOffset.memory.y < scrollView.contentOffset.y {
println(\"Going up!\")
} else {
println(\"Going down!\")
}
}
回答7:
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset {
CGPoint targetPoint = *targetContentOffset;
CGPoint currentPoint = scrollView.contentOffset;
if (targetPoint.y > currentPoint.y) {
NSLog(@\"up\");
}
else {
NSLog(@\"down\");
}
}
回答8:
This is what it worked for me (in Objective-C):
- (void)scrollViewDidScroll:(UIScrollView *)scrollView{
NSString *direction = ([scrollView.panGestureRecognizer translationInView:scrollView.superview].y >0)?@\"up\":@\"down\";
NSLog(@\"%@\",direction);
}
回答9:
Swift 4:
For the horizontal scrolling you can simply do :
if scrollView.panGestureRecognizer.translation(in: scrollView.superview).x > 0 {
print(\"left\")
} else {
print(\"right\")
}
For vertical scrolling change .x
with .y
回答10:
Alternatively, it is possible to observe key path \"contentOffset\". This is useful when it\'s not possible for you to set/change the delegate of the scroll view.
[yourScrollView addObserver:self forKeyPath:@\"contentOffset\" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];
After adding the observer, you could now:
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{
CGFloat newOffset = [[change objectForKey:@\"new\"] CGPointValue].y;
CGFloat oldOffset = [[change objectForKey:@\"old\"] CGPointValue].y;
CGFloat diff = newOffset - oldOffset;
if (diff < 0 ) { //scrolling down
// do something
}
}
Do remember to remove the observer when needed. e.g. you could add the observer in viewWillAppear and remove it in viewWillDisappear
回答11:
Here is my solution for behavior like in @followben answer, but without loss with slow start (when dy is 0)
@property (assign, nonatomic) BOOL isFinding;
@property (assign, nonatomic) CGFloat previousOffset;
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
self.isFinding = YES;
}
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
if (self.isFinding) {
if (self.previousOffset == 0) {
self.previousOffset = self.tableView.contentOffset.y;
} else {
CGFloat diff = self.tableView.contentOffset.y - self.previousOffset;
if (diff != 0) {
self.previousOffset = 0;
self.isFinding = NO;
if (diff > 0) {
// moved up
} else {
// moved down
}
}
}
}
}
回答12:
In swift:
func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
if scrollView.panGestureRecognizer.translation(in: scrollView).y < 0 {
print(\"down\")
} else {
print(\"up\")
}
}
You can do it also in scrollViewDidScroll.
回答13:
I checked some of the answer and elaborated on AnswerBot answer by wrapping everything in a drop in UIScrollView category. The \"lastContentOffset\" is saved inside the uiscrollview instead and then its just a matter of calling :
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
[scrollView setLastContentOffset:scrollView.contentOffset];
}
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
if (scrollView.scrollDirectionX == ScrollDirectionRight) {
//Do something with your views etc
}
if (scrollView.scrollDirectionY == ScrollDirectionUp) {
//Do something with your views etc
}
}
Source code at
https://github.com/tehjord/UIScrollViewScrollingDirection
回答14:
I prefer to do some filtering, based on @memmons\'s answer
In Objective-C:
// in the private class extension
@property (nonatomic, assign) CGFloat lastContentOffset;
// in the class implementation
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
if (fabs(self.lastContentOffset - scrollView.contentOffset.x) > 20 ) {
self.lastContentOffset = scrollView.contentOffset.x;
}
if (self.lastContentOffset > scrollView.contentOffset.x) {
// Scroll Direction Left
// do what you need to with scrollDirection here.
} else {
// omitted
// if (self.lastContentOffset < scrollView.contentOffset.x)
// do what you need to with scrollDirection here.
// Scroll Direction Right
}
}
When tested in - (void)scrollViewDidScroll:(UIScrollView *)scrollView
:
NSLog(@\"lastContentOffset: --- %f, scrollView.contentOffset.x : --- %f\", self.lastContentOffset, scrollView.contentOffset.x);
self.lastContentOffset
changes very fast, the value gap is nearly 0.5f.
It is not necessary.
And occasionally, when handled in accurate condition, your orientation maybe get lost. (implementation statements skipped sometimes)
such as :
- (void)scrollViewDidScroll:(UIScrollView *)scrollView{
CGFloat viewWidth = scrollView.frame.size.width;
self.lastContentOffset = scrollView.contentOffset.x;
// Bad example , needs value filtering
NSInteger page = scrollView.contentOffset.x / viewWidth;
if (page == self.images.count + 1 && self.lastContentOffset < scrollView.contentOffset.x ){
// Scroll Direction Right
// do what you need to with scrollDirection here.
}
....
In Swift 4:
var lastContentOffset: CGFloat = 0
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if (abs(lastContentOffset - scrollView.contentOffset.x) > 20 ) {
lastContentOffset = scrollView.contentOffset.x;
}
if (lastContentOffset > scrollView.contentOffset.x) {
// Scroll Direction Left
// do what you need to with scrollDirection here.
} else {
// omitted
// if (self.lastContentOffset < scrollView.contentOffset.x)
// do what you need to with scrollDirection here.
// Scroll Direction Right
}
}
回答15:
When paging is turned on,you could use these code.
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
self.lastPage = self.currentPage;
CGFloat pageWidth = _mainScrollView.frame.size.width;
self.currentPage = floor((_mainScrollView.contentOffset.x - pageWidth / 2) / pageWidth) + 1;
if (self.lastPage < self.currentPage) {
//go right
NSLog(@\"right\");
}else if(self.lastPage > self.currentPage){
//go left
NSLog(@\"left\");
}else if (self.lastPage == self.currentPage){
//same page
NSLog(@\"same page\");
}
}
回答16:
Codes explains itself I guess.
CGFloat difference1 and difference2 declared in same class private interface.
Good if contentSize stays same.
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
CGFloat contentOffSet = scrollView.contentOffset.y;
CGFloat contentHeight = scrollView.contentSize.height;
difference1 = contentHeight - contentOffSet;
if (difference1 > difference2) {
NSLog(@\"Up\");
}else{
NSLog(@\"Down\");
}
difference2 = contentHeight - contentOffSet;
}
回答17:
Ok so for me this implementation is working really good:
@property (nonatomic, assign) CGPoint lastContentOffset;
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
_lastContentOffset.x = scrollView.contentOffset.x;
_lastContentOffset.y = scrollView.contentOffset.y;
}
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
if (_lastContentOffset.x < (int)scrollView.contentOffset.x) {
// moved right
NSLog(@\"right\");
}
else if (_lastContentOffset.x > (int)scrollView.contentOffset.x) {
// moved left
NSLog(@\"left\");
}else if (_lastContentOffset.y<(int)scrollView.contentOffset.y){
NSLog(@\"up\");
}else if (_lastContentOffset.y>(int)scrollView.contentOffset.y){
NSLog(@\"down\");
[self.txtText resignFirstResponder];
}
}
So this will fire textView to dismiss after drag ends
回答18:
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset {
NSLog(@\"px %f py %f\",velocity.x,velocity.y);}
Use this delegate method of scrollview.
If y co-ordinate of velocity is +ve scroll view scrolls downwards and if it is -ve scrollview scrolls upwards.
Similarly left and right scroll can be detected using x co-ordinate.
回答19:
Short & Easy would be, just check the velocity value, if its greater than zero then its scrolling left else right:
func scrollViewWillEndDragging(scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
var targetOffset = Float(targetContentOffset.memory.x)
println(\"TargetOffset: \\(targetOffset)\")
println(velocity)
if velocity.x < 0 {
scrollDirection = -1 //scrolling left
} else {
scrollDirection = 1 //scrolling right
}
}
回答20:
If you work with UIScrollView and UIPageControl, this method will also change the PageControl\'s page view.
func scrollViewWillEndDragging(scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
let targetOffset = targetContentOffset.memory.x
let widthPerPage = scrollView.contentSize.width / CGFloat(pageControl.numberOfPages)
let currentPage = targetOffset / widthPerPage
pageControl.currentPage = Int(currentPage)
}
Thanks @Esq \'s Swift code.
回答21:
Swift 2.2 Simple solution which tracks single and multiple directions without any loss.
// Keep last location with parameter
var lastLocation:CGPoint = CGPointZero
// We are using only this function so, we can
// track each scroll without lose anyone
override func scrollViewWillBeginDragging(scrollView: UIScrollView) {
let currentLocation = scrollView.contentOffset
// Add each direction string
var directionList:[String] = []
if lastLocation.x < currentLocation.x {
//print(\"right\")
directionList.append(\"Right\")
} else if lastLocation.x > currentLocation.x {
//print(\"left\")
directionList.append(\"Left\")
}
// there is no \"else if\" to track both vertical
// and horizontal direction
if lastLocation.y < currentLocation.y {
//print(\"up\")
directionList.append(\"Up\")
} else if lastLocation.y > currentLocation.y {
//print(\"down\")
directionList.append(\"Down\")
}
// scrolled to single direction
if directionList.count == 1 {
print(\"scrolled to \\(directionList[0]) direction.\")
} else if directionList.count > 0 { // scrolled to multiple direction
print(\"scrolled to \\(directionList[0])-\\(directionList[1]) direction.\")
}
// Update last location after check current otherwise,
// values will be same
lastLocation = scrollView.contentOffset
}