2012-10-05 4 views
9

Tôi muốn triển khai khả năng điều hướng ngược lại và thứ tư trong webView của mình bằng cách sử dụng Event swipeWithEvent. Tuy nhiên, tôi không có ý tưởng làm thế nào tôi giả sử để sử dụng này.Cách sử dụng đúng swipeWithEvent để điều hướng một webView, Obj-C

Tôi có một webView chính và hai phương pháp điều hướng ngược lại và thứ tư. Một vấn đề lớn ở đây là tôi không chắc chắn làm thế nào để viết câu hỏi này. Tôi chỉ cần biết cách nhận ra cử chỉ vuốt trên webView của tôi và gọi hai phương thức của tôi. Tương tự như Safari, Google Chrome và Mozilla Firefox. Cảm ơn.

EDIT Tôi đã triển khai các phương pháp này giả sử cho phép tôi vuốt lại và tiến.

- (void)swipeWithEvent:(NSEvent *)event { 
    NSLog(@"Swipe With Event"); 
    CGFloat x = [event deltaX]; 
    //CGFloat y = [event deltaY]; 

    if (x != 0) { 
     (x > 0) ? [self goBack:self] : [self goForward:self]; 
    } 
} 


-(BOOL)recognizeTwoFingerGestures 
{ 
    NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults]; 
    return [defaults boolForKey:@"AppleEnableSwipeNavigateWithScrolls"]; 
} 

- (void)beginGestureWithEvent:(NSEvent *)event 
{ 
    if (![self recognizeTwoFingerGestures]) 
     return; 

    NSSet *touches = [event touchesMatchingPhase:NSTouchPhaseAny inView:nil]; 

    self.twoFingersTouches = [[NSMutableDictionary alloc] init]; 

    for (NSTouch *touch in touches) { 
     [twoFingersTouches setObject:touch forKey:touch.identity]; 
    } 
} 

- (void)endGestureWithEvent:(NSEvent *)event 
{ 
    if (!twoFingersTouches) return; 

    NSSet *touches = [event touchesMatchingPhase:NSTouchPhaseAny inView:nil]; 

    // release twoFingersTouches early 
    NSMutableDictionary *beginTouches = [twoFingersTouches copy]; 
    self.twoFingersTouches = nil; 

    NSMutableArray *magnitudes = [[NSMutableArray alloc] init]; 

    for (NSTouch *touch in touches) 
    { 
     NSTouch *beginTouch = [beginTouches objectForKey:touch.identity]; 

     if (!beginTouch) continue; 

     float magnitude = touch.normalizedPosition.x - beginTouch.normalizedPosition.x; 
     [magnitudes addObject:[NSNumber numberWithFloat:magnitude]]; 
    } 

    // Need at least two points 
    if ([magnitudes count] < 2) return; 

    float sum = 0; 

    for (NSNumber *magnitude in magnitudes) 
     sum += [magnitude floatValue]; 

    // Handle natural direction in Lion 
    BOOL naturalDirectionEnabled = [[[NSUserDefaults standardUserDefaults] valueForKey:@"com.apple.swipescrolldirection"] boolValue]; 

    if (naturalDirectionEnabled) 
     sum *= -1; 

    // See if absolute sum is long enough to be considered a complete gesture 
    float absoluteSum = fabsf(sum); 

    if (absoluteSum < kSwipeMinimumLength) return; 

    // Handle the actual swipe 
    if (sum > 0) 
    { 
     [self goForward:self]; 
    } else 
    { 
     [self goBack:self]; 
    } 


} 

Tuy nhiên, mã này không hoạt động. Nó dường như không được gọi là tất cả.

+0

Bạn có thể tải lên một ứng dụng mẫu trong đó '-swipeWithEvent:' không được gọi? – Kentzo

+1

Ở đây. https://www.dropbox.com/s/sukkzvefwi836kr/swipeProject.zip?dl=1 – Josiah

Trả lời

19

Đối với hệ điều hành hiện đại (Sư tử và mới hơn), đây là một trong những "Làm cách nào để thực hiện X với Y?" câu hỏi mà câu trả lời là "Không sử dụng Y."

-swipeWithEvent: được sử dụng để triển khai trượt bàn phím kiểu 10,6, nơi nội dung trên màn hình không di chuyển bằng thao tác vuốt. Hầu hết các trackpad của Mac không được định cấu hình để cho phép loại vuốt này nữa; tùy chọn "Trượt giữa các trang" trong ngăn trước của bảng điều khiển phải được đặt thành "vuốt bằng ba ngón tay" để có sẵn và không phải cài đặt mặc định cũng như cài đặt chung để người dùng thay đổi.

"swipes dạng lỏng" kiểu Lion thay vì xuất hiện dưới dạng sự kiện cuộn. Các dự án mẫu PictureSwiper trong Xcode SDK của bạn là một nơi tốt để bắt đầu, nhưng như một cái nhìn tổng quan, đây là những gì bạn cần làm:

Nếu bạn chỉ cần hỗ trợ 10.8+

Sử dụng một NSPageController trong ngăn xếp chế độ lịch sử .

Nếu bạn cần hỗ trợ 10,7

  1. Nghiêm túc xem xét chỉ 10.8+ hỗ trợ, ít nhất là cho tính năng này. Thực hiện nó bằng tay là một mớ hỗn độn lớn. Đừng nói rằng tôi đã không cảnh báo bạn.

  2. Tạo chế độ xem tùy chỉnh là giám sát cho bất kỳ chế độ xem nào bạn muốn có thể vuốt được.

  3. Ghi đè -wantsScrollEventsForSwipeTrackingOnAxis: để trả lại YES cho trục thích hợp. Nếu bạn đang thực hiện điều hướng quay lại/chuyển tiếp kiểu Safari, đó là NSEventGestureAxisHorizontal.

  4. Hít thật sâu; điều này là một doozy.

  5. Ghi đè -scrollWheel: trong chế độ xem tùy chỉnh của bạn. Ghi đè của bạn nên gọi -trackSwipeEventWithOptions:dampenAmountThresholdMin:max:usingHandler:. Nói chung, ngưỡng tối thiểu giảm thiểu là số lượng dữ liệu người dùng có thể vuốt sang trái và số lượng tối đa là số lượng người dùng có thể vuốt sang phải. Trình xử lý là một khối được gọi nhiều lần khi người dùng vuốt; cần:

    1. Đặt NSImageView đặt một ảnh chụp màn hình của trang bạn sẽ quay lại/chuyển tiếp phía sau chế độ xem nội dung.
    2. Di chuyển chế độ xem nội dung để phù hợp với chuyển động của người dùng. Lưu ý rằng tham số gestureAmount là, giống như ngưỡng số tiền giảm, số lượng mục (phân số và có thể âm); bạn phải nhân nó với chiều rộng xem để định vị chính xác chế độ xem nội dung.
    3. Nếu giai đoạn cử chỉ là NSEventPhaseEnded, hãy đánh giá gestureAmount để xác định xem người dùng có hoàn thành cử chỉ hay không. Nếu họ không làm, hãy tạo hiệu ứng cho chế độ xem nội dung trở lại vị trí; nếu họ đã làm, hãy đặt chế độ xem nội dung trở lại tại chỗ mà không có hoạt ảnh và cập nhật nó để khớp với ảnh chụp màn hình.

Như bạn thấy, trên thực tế việc thực hiện xử lý là rất có liên quan, và tôi thậm chí còn không được mô tả tất cả các chi tiết cụ thể. Ngay cả với tất cả các chi tiết này, một lập trình viên có tay nghề cao có thể sẽ phải mất một vài ngày để nhận được điều này vừa phải. Mẫu PictureSwiper trong SDK 10.7 là một nơi tốt để bắt đầu. (Phiên bản 10.8 của PictureSwiper sử dụng NSPageController Cũng giống như bạn phải làm..)

+0

Ngoại trừ, ứng dụng của tôi hỗ trợ Snow Leopard +. Vì vậy, không thể sử dụng NSPageController. – Josiah

+0

@AceLegend: Phần lớn câu trả lời của Brent là làm thế nào để làm điều đó mà không có NSPageController (nếu bạn đang sử dụng NSPageController, bạn dừng ở bước 1). Bạn sẽ có nhiều việc phải làm hơn khi các API 10.7 và sau đó không khả dụng. –

+0

Tôi hiểu. Có lẽ tôi có thể thực hiện NSPageController chỉ dành cho OS X 10.8. Tôi không chỉ là người hâm mộ sử dụng mã dành riêng cho hệ điều hành. Tôi thấy nó gây ra vấn đề. – Josiah

9

Từ the 10.7 release notes:

Lỏng Tracking Swipe - API

API sau cho phép theo dõi cử chỉ bánh xe di chuyển các sự kiện dưới dạng cử chỉ vuốt chất lỏng. Tương tự như iOS, NSScrollView sẽ trả lại một lần nếu cần, sau đó tùy chọn chuyển sang các sự kiện bánh xe cuộn cử chỉ để bộ điều khiển của bạn có thể sử dụng API này để theo dõi cử chỉ cuộn như cử chỉ vuốt chất lỏng.

ScrollWheel NSEvents hiện phản hồi với thông điệp pha. Có 3 loại cuộn:

1) Cuộn cử chỉ - bắt đầu bằng NSEventPhaseBegan, có một số sự kiện NSEventPhaseChanged và chấm dứt khi người dùng nhấc ngón tay lên bằng NSEventPhaseEnded.

2) Cuộn theo thời gian - chúng có pha NSEventPhase không có, nhưng chúng có một xung lượng của NSEventPhaseBegan/NSEventPhaseChanged/NSEventPhaseEnded.

3) Cuộn cũ - các sự kiện bánh xe cuộn này có pha NSEventPhaseNone và một xung lượng của NSEventPhaseNone. Không có cách nào để xác định khi nào người dùng bắt đầu, cũng không dừng lại, thực hiện các cuộn kế thừa.

NSScrollView xử lý tất cả các sự kiện bánh xe cuộn cử chỉ và không chuyển chúng lên chuỗi phản hồi. Thông thường, việc theo dõi thao tác vuốt được thực hiện cao hơn trong chuỗi trả lời, ví dụ ở cấp WindowController. Để đạt được một phong cách iOS "trả lại khi không ở cạnh, nếu không swipe" hành vi, bạn cần phải thông báo cho NSScrollView rằng nó sẽ chuyển tiếp tin nhắn bánh xe di chuyển khi thích hợp. Thay vì đặt thủ công một thuộc tính trên NSScrollView, NSResponder của bạn có thể thực hiện phương thức sau và trả về YES.

- (BOOL)wantsScrollEventsForSwipeTrackingOnAxis:(NSEventGestureAxis)axis; 

Khi bộ điều khiển thích hợp nhận được một -scrollWheel: tin nhắn với một giai đoạn NSEventNone thuốc, bạn có thể gọi được thông báo sau trên đó dụ kiện để theo dõi swipe hoặc di chuyển cho cả hai hoàn thành sử dụng các sự kiện này, và hoàn thành hoạt hình .

enum { 
    NSEventSwipeTrackingLockDirection = 0x1 << 0, 
    NSEventSwipeTrackingClampGestureAmount = 0x1 << 1 
}; 

typedef NSUInteger NSEventSwipeTrackingOptions; 

@interface NSEvent ... 
- (void)trackSwipeEventWithOptions:(NSEventSwipeTrackingOptions)options 
      dampenAmountThresholdMin:(CGFloat)minDampenThreshold 
           max:(CGFloat)maxDampenThreshold 
         usingHandler:(void (^)(CGFloat gestureAmount, NSEventPhase phase, > BOOL isComplete, BOOL *stop))handler; 
... 

Dưới đây là ví dụ mã giả để vuốt bộ sưu tập ảnh như ứng dụng Ảnh trên iOS.

- (BOOL)wantsScrollEventsForSwipeTrackingOnAxis:(NSEventGestureAxis)axis { 
    return (axis == NSEventGestureAxisHorizontal) ? YES : NO; } 
- (void)scrollWheel:(NSEvent *)event { 
    // NSScrollView is instructed to only forward horizontal scroll gesture events (see code above). However, depending 
    // on where your controller is in the responder chain, it may receive other scrollWheel events that we don't want 
    // to track as a fluid swipe because the event wasn't routed though an NSScrollView first. 
    if ([event phase] == NSEventPhaseNone) return; // Not a gesture scroll event. 
    if (fabsf([event scrollingDeltaX]) <= fabsf([event scrollingDeltaY])) return; // Not horizontal 
    // If the user has disabled tracking scrolls as fluid swipes in system preferences, we should respect that. 
    // NSScrollView will do this check for us, however, depending on where your controller is in the responder chain, 
    // it may scrollWheel events that are not filtered by an NSScrollView. 
    if (![NSEvent isSwipeTrackingFromScrollEventsEnabled]) return; 
    if (_swipeAnimationCanceled && *_swipeAnimationCanceled == NO) { 
     // A swipe animation is still in gestureAmount. Just kill it. 
     *_swipeAnimationCanceled = YES; 
     _swipeAnimationCanceled = NULL; 
    } 
    CGFloat numPhotosToLeft = // calc num of photos we can move to the left and negate 
    CGFloat numPhotosToRight = // calc num of photos we can move to the right 
    __block BOOL animationCancelled = NO; 
    [event trackSwipeEventWithOptions:0 dampenAmountThresholdMin:numPhotosToLeft max:numPhotosToRight 
         usingHandler:^(CGFloat gestureAmount, NSEventPhase phase, BOOL isComplete, BOOL *stop) { 
     if (animationCancelled) { 
      *stop = YES; 
      // Tear down animation overlay 
      return; 
     } 
     if (phase == NSEventPhaseBegan) { 
      // Setup animation overlay layers 
     } 
     // Update animation overlay to match gestureAmount 
     if (phase == NSEventPhaseEnded) { 
      // The user has completed the gesture successfully. 
      // This handler will continue to get called with updated gestureAmount 
      // to animate to completion, but just in case we need 
      // to cancel the animation (due to user swiping again) setup the 
      // controller/view to point to the new content/index/whatever 
     } else if (phase == NSEventPhaseCancelled) { 
      // The user has completed the gesture un-successfully. 
      // This handler will continue to get called with updated gestureAmount 
      // But we don't need to change the underlying controller/view settings. 
     } 
     if (isComplete) { 
      // Animation has completed and gestureAmount is either -1, 0, or 1. 
      // This handler block will be released upon return from this iteration. 
      // Note: we already updated our view to use the new (or same) content 
      // above. So no need to do that here. Just... 
      // Tear down animation overlay here 
      self->_swipeAnimationCanceled = NULL; 
     } 
    }]; 
    // We keep a pointer to a BOOL __block variable so that we can cancel our 
    // block handler at any time. Note: You must assign the BOOL pointer to your 
    // ivar after block creation and copy! 
    self->_swipeAnimationCanceled = &animationCancelled; } 

giải pháp khác sẽ được trực tiếp xử lý các sự kiện liên lạc trực tiếp để nhận ra cử chỉ. Để biết thêm thông tin, hãy xem xét documentation.

+1

Tôi có đọc sai không, nhưng có vẻ như ví dụ của bạn yêu cầu bạn phải có một NSScrollView. Tôi không có. Ngoài ra, có vẻ hơi phức tạp cho những gì tôi cần. Tôi muốn theo dõi swiping, nhưng thực sự tôi chỉ cần biết khi người dùng "hoàn thành" một swipe trong hai hướng. Sau đó tôi chỉ muốn thực hiện một phương thức. Đó là tất cả. – Josiah

+1

@AceLegend Bạn đang đọc sai, nó không yêu cầu NSScrollView. Nó ghi chú những gì bạn cần có NSScrollView. – Kentzo

+0

Được rồi, tôi rất vui vì điều đó. Nhưng con người, tôi ngạc nhiên về sự phức tạp này. Tôi đang vượt qua các ngón tay của tôi để nhận dạng cử chỉ cho OS X 10.9 SDK. :) – Josiah