2011-10-06 34 views
25

Tóm lại, tôi cần phải biết chính xác khi nào scrollview ngừng cuộn. Bằng cách 'ngừng di chuyển', tôi có nghĩa là khoảnh khắc mà nó không còn di chuyển và không bị xúc động nữa.Làm thế nào để biết chính xác thời điểm cuộn UIScrollView đã dừng?

Tôi đã làm việc trên một lớp con UIScrollView ngang (dành cho iOS 4) với các tab lựa chọn trong đó. Một trong những yêu cầu của nó là nó dừng cuộn bên dưới một tốc độ nhất định để cho phép tương tác người dùng nhanh hơn. Nó cũng sẽ snap vào đầu của một tab. Nói cách khác, khi người dùng thả scrollview và tốc độ của nó xuống thấp, nó sẽ chụp một vị trí. Tôi đã thực hiện điều này và nó hoạt động, nhưng có một lỗi trong đó.

Điều tôi hiện có:

Scrollview là đại biểu của riêng nó. ở mọi cuộc gọi đến scrollViewDidScroll :, nó làm mới nó biến tốc độ liên quan đến:

-(void)refreshCurrentSpeed 
{ 
    float currentOffset = self.contentOffset.x; 
    NSTimeInterval currentTime = [[NSDate date] timeIntervalSince1970]; 

    deltaOffset = (currentOffset - prevOffset); 
    deltaTime = (currentTime - prevTime);  
    currentSpeed = deltaOffset/deltaTime; 
    prevOffset = currentOffset; 
    prevTime = currentTime; 

    NSLog(@"deltaOffset is now %f, deltaTime is now %f and speed is %f",deltaOffset,deltaTime,currentSpeed); 
} 

Sau đó tiến hành để chụp nếu cần thiết:

-(void)snapIfNeeded 
{ 
    if(canStopScrolling && currentSpeed <70.0f && currentSpeed>-70.0f) 
    { 
     NSLog(@"Stopping with a speed of %f points per second", currentSpeed); 
     [self stopMoving]; 

     float scrollDistancePastTabStart = fmodf(self.contentOffset.x, (self.frame.size.width/3)); 
     float scrollSnapX = self.contentOffset.x - scrollDistancePastTabStart; 
     if(scrollDistancePastTabStart > self.frame.size.width/6) 
     { 
      scrollSnapX += self.frame.size.width/3; 
     } 
     float maxSnapX = self.contentSize.width-self.frame.size.width; 
     if(scrollSnapX>maxSnapX) 
     { 
      scrollSnapX = maxSnapX; 
     } 
     [UIView animateWithDuration:0.3 
         animations:^{self.contentOffset=CGPointMake(scrollSnapX, self.contentOffset.y);} 
         completion:^(BOOL finished){[self stopMoving];} 
     ]; 
    } 
    else 
    { 
     NSLog(@"Did not stop with a speed of %f points per second", currentSpeed); 
    } 
} 

-(void)stopMoving 
{ 
    if(self.dragging) 
    { 
     [self setContentOffset:CGPointMake(self.contentOffset.x, self.contentOffset.y) animated:NO]; 
    } 
    canStopScrolling = NO; 
} 

Sau đây là các phương pháp đại biểu:

-(void)scrollViewWillBeginDragging:(UIScrollView *)scrollView 
{ 
    canStopScrolling = NO; 
    [self refreshCurrentSpeed]; 
} 

-(void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate 
{ 
    canStopScrolling = YES; 
    NSLog(@"Did end dragging"); 
    [self snapIfNeeded]; 
} 

- (void)scrollViewDidScroll:(UIScrollView *)scrollView 
{ 
    [self refreshCurrentSpeed]; 
    [self snapIfNeeded]; 
} 

này hoạt động tốt hầu hết thời gian, ngoại trừ trong hai trường hợp: 1. Khi người dùng cuộn mà không cần nhả ngón tay của mình và cho phép di chuyển gần đúng lúc sau khi di chuyển, nó thường xuyên chụp vị trí của nó như nó được cho là, nhưng rất nhiều lần, thì không. Nó thường mất một vài nỗ lực để làm cho nó xảy ra. Giá trị Odd cho thời gian (rất thấp) và/hoặc khoảng cách (khá cao) xuất hiện ở bản phát hành, gây ra một giá trị tốc độ cao trong khi scrollView là, trong thực tế, gần như hoặc hoàn toàn văn phòng phẩm. 2. Khi người dùng chạm vào scrollview để dừng chuyển động của nó, có vẻ như scrollview đặt contentOffset thành vị trí trước đó của nó. Dịch chuyển dịch này dẫn đến giá trị tốc độ rất cao. Điều này có thể được cố định bằng cách kiểm tra nếu đồng bằng trước đó là currentDelta * -1, nhưng tôi muốn một giải pháp ổn định hơn.

Tôi đã thử sử dụng didEndDecelerating, nhưng khi trục trặc xảy ra, nó không được gọi. Điều này có thể khẳng định rằng nó đã dừng lại. Dường như không có phương thức ủy nhiệm nào được gọi khi scrollview ngừng di chuyển hoàn toàn.

Nếu bạn muốn xem các trục trặc cho mình, sau đây là một số mã để điền vào scrollview với các tab:

@interface UIScrollView <UIScrollViewDelegate> 
{ 
    bool canStopScrolling; 
    float prevOffset; 
    float deltaOffset; //remembered for debug purposes 
    NSTimeInterval prevTime; 
    NSTimeInterval deltaTime; //remembered for debug purposes 
    float currentSpeed; 
} 

-(void)stopMoving; 
-(void)snapIfNeeded; 
-(void)refreshCurrentSpeed; 

@end 


@implementation TabScrollView 

-(id) init 
{ 
    self = [super init]; 
    if(self) 
    { 
     self.delegate = self; 
     self.frame = CGRectMake(0.0f,0.0f,320.0f,40.0f); 
     self.backgroundColor = [UIColor grayColor]; 
     float tabWidth = self.frame.size.width/3; 
     self.contentSize = CGSizeMake(100*tabWidth, 40.0f); 
     for(int i=0; i<100;i++) 
     { 
      UIView *view = [[UIView alloc] init]; 
      view.frame = CGRectMake(i*tabWidth,0.0f,tabWidth,40.0f); 
      view.backgroundColor = [UIColor colorWithWhite:(float)(i%2) alpha:1.0f]; 
      [self addSubview:view]; 
     } 
    } 
    return self; 
} 

@end 

Một phiên bản ngắn hơn của câu hỏi này: làm thế nào để biết khi nào scrollview ngừng di chuyển? didEndDecelerating: không được gọi khi bạn thả nó cố định, didEndDragging: xảy ra rất nhiều trong quá trình cuộn và kiểm tra tốc độ không đáng tin cậy do 'nhảy' kỳ lạ này đặt tốc độ thành một cái gì đó ngẫu nhiên.

+0

Bạn có thể kết hợp 'didEndDragging' với sự kiện chạm để khi người dùng chạm vào chế độ xem bạn đặt cờ và đặt lại khi họ xóa ngón tay của họ. Sau đó, miễn là cờ được đặt, bạn không chạy logic có liên quan trong 'didEndDragging:'. – FreeAsInBeer

+0

Cảm ơn bạn đã phản hồi. Tuy nhiên, tôi không chắc chắn về lá cờ mà bạn đề cập đến. Nó xuất hiện với tôi rằng bạn có nghĩa là cờ cho thấy khi scrollview đã được phát hành một lần, nhưng tôi không thể tìm ra cách đó sẽ hữu ích, xem xét người dùng phát hành scrollview nhiều lần trong khi di chuyển, hoặc chỉ ở một điểm tĩnh mà không có nhấc ngón tay lên. – Aberrant

Trả lời

30

Tôi tìm thấy một giải pháp:

-(void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate 

Tôi không để ý rằng bit cuối cùng trước khi, willDecelerate. Đó là sai khi scrollView là văn phòng phẩm khi kết thúc liên lạc. Kết hợp với kiểm tra tốc độ nói trên, tôi có thể chụp cả hai khi nó chậm (và không bị chạm) hoặc khi nó dừng.

Đối với bất kỳ ai không thực hiện bất kỳ thao tác chụp nào nhưng cần biết khi di chuyển dừng lại, didEndDecelerating sẽ được gọi vào cuối chuyển động cuộn. Kết hợp với séc trên willDecelerate trong didEndDragging, bạn sẽ biết thời điểm cuộn đã dừng.

+0

là điều này cũng làm việc khi pagingEnabled của scrollView là Có? đây là bài viết của tôi về vấn đề của tôi. http://stackoverflow.com/questions/9337996/objective-c-uiscrollview-pagingenabled-when-next-page-enters-start-the-anima – SeongHo

+0

@SeongHo Tôi khá chắc chắn điều đó. Tôi hiện không thể kiểm tra nó mặc dù, và sẽ không được cho một thời gian khá. – Aberrant

20

[Câu trả lời đã chỉnh sửa] Đây là những gì tôi sử dụng - nó xử lý tất cả các trường hợp cạnh. Bạn cần một ngà voi để giữ trạng thái, và như thể hiện trong các ý kiến, có những cách khác để xử lý điều này.

- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView 
{ 
    //[super scrollViewWillBeginDragging:scrollView]; // pull to refresh 

    self.isScrolling = YES; 
    NSLog(@"+scrollViewWillBeginDragging"); 
} 

- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate 
{ 
    //[super scrollViewDidEndDragging:scrollView willDecelerate:decelerate]; // pull to refresh 

    if(!decelerate) { 
     self.isScrolling = NO; 
    } 
    NSLog(@"%@scrollViewDidEndDragging", self.isScrolling ? @"" : @"-"); 
} 
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView 
{ 
    self.isScrolling = NO; 
    NSLog(@"-scrollViewDidEndDecelerating"); 
} 

- (void)scrollViewDidScrollToTop:(UIScrollView *)scrollView 
{ 
    self.isScrolling = NO; 
    NSLog(@"-scrollViewDidScrollToTop"); 
} 

- (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView 
{ 
    self.isScrolling = NO; 
    NSLog(@"-scrollViewDidEndScrollingAnimation"); 
} 

Tôi tạo ra một dự án thực sự đơn giản sử dụng đoạn mã trên, do đó khi một người tương tác với một scrollview (bao gồm cả một WebView), nó ức chế quá trình làm việc chuyên sâu cho đến khi người dùng ngừng tương tác với scrollview VÀ scrollview có ngừng cuộn. Giống như 50 dòng mã: ScrollWatcher

+2

Điều này, thưa ông, là giải pháp sạch nhất cho vấn đề ở bàn tay, mà tôi đã thấy. Cảm ơn bạn. – thomax

+0

Để trả lời chỉnh sửa của bạn, bạn có thể như sau: https://github.com/lmirosevic/GBToolbox/commit/87be97a5eff702ef9ef0361fcb0539f36eb4984f Bạn chỉ cần gọi 'ExecuteAfterScrolling (^ {NSLog (@ "heavy work");}); 'và không có cần phải gây rối với các trạng thái boolean và xả mã của bạn bằng các kiểm tra có điều kiện. – lms

+0

@lms Bạn có thể giải thích lý do tại sao nó hoạt động không? Dường như phương pháp chỉ chạy trong NSTimer –

2

Phương thức ủy quyền được đề cập trong bài đăng này không giúp tôi. Tôi tìm thấy một câu trả lời mà phát hiện thức cuối cuộn trong scrollViewDidScroll: Link là here

+0

+1, điều này đã giúp tôi, cảm ơn bạn – NAZIK

18

Dưới đây là làm thế nào để kết hợp scrollViewDidEndDeceleratingscrollViewDidEndDragging:willDecelerate để thực hiện một số hoạt động khi di chuyển đã hoàn tất:

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView 
{ 
    [self stoppedScrolling]; 
} 

- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView 
        willDecelerate:(BOOL)decelerate 
{ 
    if (!decelerate) { 
     [self stoppedScrolling]; 
    } 
} 

- (void)stoppedScrolling 
{ 
    // done, do whatever 
} 
1

Tôi đã trả lời câu hỏi này trên blog của tôi trong đó vạch ra làm thế nào để làm điều đó mà không có vấn đề.

Điều này liên quan đến việc 'chặn các đại biểu' thực hiện một số việc, sau đó chuyển các thông điệp của đại biểu đến nơi họ dự định. Điều này là cần bởi vì có một vài trường hợp ngọn lửa đại biểu nếu nó di chuyển theo chương trình, hoặc không, hoặc cả hai, và vì vậy dù sao, tốt nhất để chỉ cần nhìn vào liên kết dưới đây:

How to know when a UIScrollView is scrolling

Đó là một chút khó khăn , nhưng tôi đã đưa ra một công thức ở đó và đã giải thích chi tiết các bộ phận.

Các vấn đề liên quan