水平スクロールのみが許可されているUIScrollView
があり、ユーザーがどの方向(左、右)をスクロールするかを知りたい。私がしたことは、UIScrollView
をサブクラス化し、touchesMoved
メソッドをオーバーライドすることでした。
- (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");
}
}
しかし、このメソッドは、移動してもまったく呼び出されないことがあります。どう思いますか?
方向の決定は非常に簡単ですが、方向はジェスチャーの過程で数回変化する可能性があることに注意してください。たとえば、ページングがオンになっているスクロールビューがあり、ユーザーが次のページにスワイプすると、最初の方向は右になりますが、バウンスがオンになっていると、方向がまったくない状態になり、その後、しばらく左に進みます。
方向を決定するには、UIScrollView scrollViewDidScroll
デリゲートを使用する必要があります。このサンプルでは、lastContentOffset
という名前の変数を作成しました。この変数を使用して、現在のコンテンツオフセットと前のコンテンツオフセットを比較します。大きい場合、scrollViewは右にスクロールしています。小さい場合、scrollViewは左にスクロールします。
// 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.
}
次の列挙型を使用して方向を定義しています。最初の値をScrollDirectionNoneに設定すると、変数を初期化するときにその方向をデフォルトにするという追加の利点があります。
typedef NS_ENUM(NSInteger, ScrollDirection) {
ScrollDirectionNone,
ScrollDirectionRight,
ScrollDirectionLeft,
ScrollDirectionUp,
ScrollDirectionDown,
ScrollDirectionCrazy,
};
...ユーザーがスクロールする方向(左、右)を知りたい
その場合、iOS 5以降では、UIScrollViewDelegate
を使用してユーザーのパンジェスチャーの方向を決定します。
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
{
if ([scrollView.panGestureRecognizer translationInView:scrollView.superview].x > 0) {
// handle dragging to the right
} else {
// handle dragging to the left
}
}
scrollViewDidScroll:
を使用すると、現在の方向を見つけることができます。
方向を知りたい場合afterユーザーがスクロールを終了した場合は、次を使用します。
@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
}
}
これを追跡するために追加の変数を追加する必要はありません。このようにUIScrollView
のpanGestureRecognizer
プロパティを使用するだけです。残念ながら、これは速度が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");
}
Xおよびyコンポーネントの組み合わせを使用して、上下左右を検出できます。
解決策
func scrollViewDidScroll(scrollView: UIScrollView) {
if(scrollView.panGestureRecognizer.translationInView(scrollView.superview).y > 0)
{
print("up")
}
else
{
print("down")
}
}
IOS8 Swiftでは、このメソッドを使用しました。
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)
}
ユーザーがスクロールすると位置が変わる動的ビューがあるので、画面上の同じ場所に表示されているように見えます。また、ユーザーが上がったり下がったりするタイミングを追跡しています。
これも代替方法です。
func scrollViewWillEndDragging(scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
if targetContentOffset.memory.y < scrollView.contentOffset.y {
println("Going up!")
} else {
println("Going down!")
}
}
水平スクロールの場合は、単に次の操作を実行できます。
if scrollView.panGestureRecognizer.translation(in: scrollView.superview).x > 0 {
print("left")
} else {
print("right")
}
垂直スクロールの場合は、.x
で.y
を変更します
これは私にとってはうまくいったものです(Objective-Cで):
- (void)scrollViewDidScroll:(UIScrollView *)scrollView{
NSString *direction = ([scrollView.panGestureRecognizer translationInView:scrollView.superview].y >0)?@"up":@"down";
NSLog(@"%@",direction);
}
- (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");
}
}
または、キーパス「contentOffset」を観察することもできます。これは、スクロールビューのデリゲートを設定/変更できない場合に役立ちます。
[yourScrollView addObserver:self forKeyPath:@"contentOffset" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];
オブザーバーを追加した後、次のことができます。
- (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
}
}
必要に応じて、オブザーバーを削除することを忘れないでください。例えばviewWillAppearでオブザーバーを追加し、viewWillDisappearでオブザーバーを削除できます。
これは、@ followbenの答えのような動作に対する私のソリューションですが、スロースタートで損失なしです(dyが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
}
}
}
}
}
Swiftの場合:
func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
if scrollView.panGestureRecognizer.translation(in: scrollView).y < 0 {
print("down")
} else {
print("up")
}
}
ScrollViewDidScrollでも実行できます。
@memmonsの答えに基づいて、フィルタリングを行うことを好みます
// 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
}
}
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
でテストした場合:
NSLog(@"lastContentOffset: --- %f, scrollView.contentOffset.x : --- %f", self.lastContentOffset, scrollView.contentOffset.x);
self.lastContentOffset
は非常に速く変化し、値のギャップはほぼ0.5fです。
時には、正確な状態で取り扱われると、方向が失われることがあります。 (実装ステートメントは時々スキップされます)
といった :
- (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.
}
....
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
}
}
回答の一部を確認し、UIScrollViewカテゴリのドロップにすべてをラップして、AnswerBotの回答を詳しく説明しました。代わりに、「lastContentOffset」はuiscrollview内に保存され、次に呼び出すだけです。
- (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
}
}
https://github.com/tehjord/UIScrollViewScrollingDirection のソースコード
UIScrollViewとUIPageControlを使用する場合、このメソッドはPageControlのページビューも変更します。
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)
}
@EsqのSwiftコードに感謝します。
Swift 2.2 シンプルなソリューションどのトラックを追跡単一および複数の方向損失なし。
// 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
}
ページングをオンにすると、これらのコードを使用できます。
- (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");
}
}
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset {
NSLog(@"px %f py %f",velocity.x,velocity.y);}
Scrollviewのこのデリゲートメソッドを使用します。
速度のy座標が+ veの場合、スクロールビューは下にスクロールし、-veの場合、スクロールビューは上にスクロールします。同様に、x座標を使用して左右のスクロールを検出できます。
わかりましたので、私にとってこの実装は本当にうまく機能しています:
@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];
}
}
したがって、これはtextViewを起動して、ドラッグの終了後に終了します
Short&Easyは、ベロシティ値をチェックするだけです。ゼロより大きい場合は左にスクロールし、そうでなければ右にスクロールします。
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
}
}
コードはそれ自体を説明しています。 CGFloatの相違1と相違2は、同じクラスのプライベートインターフェイスで宣言されています。 contentSizeが同じ場合に適しています。
- (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;
}