5

I need to get notified when an UITableView's drag has come to an end.

But I'm working on an UITableView's category, so I can't use scrollViewDidEndDragging:willDecelerate: to archive this.

I tried use KVO to observe on dragging Key Path:

[self addObserver:self forKeyPath:@"dragging" options:NSKeyValueObservingOptionNew context:nil];

But observeValueForKeyPath:ofObject:change:context: didn't get called, since UITableView.dragging doesn't have and setter and this property is not compliant with KVO.

Is there any other method to archive this expect for using scrollViewDidEndDragging:willDecelerate:?

Any help is grateful! Thanks!

OpenThread
  • 2,096
  • 3
  • 28
  • 49

2 Answers2

9

Edit: My solution below was the first thing to come in mind and turned out to be rather hacky and may be unsafe to use in case Apple decides to change the internals of the UIScrollView class. See the answer suggested by Mazyod which should be safer and more straightforward.


This is implementation-dependent and may be changed by Apple in future iOS updates, but currently UIScrollView class seems to rely on gesture recognizers for managing user interaction and UITableView being a subclass of the scroll view class does the same.

If you go to UIScrollView.h of the UIKit framework, you can notice a suspicious _pan ivar which has an id type, but seems to actually be a UIPanGestureRecognizer.

So I've tried this, and it seems to work.

 [_tableView addObserver: self 
              forKeyPath: @"pan.state" 
                 options: NSKeyValueObservingOptionNew 
                 context: nil];

When dragging the table view, state of the gesture recognizer changes several times, and when you stop dragging, state receives its last change to the value of UIGestureRecognizerStateEnded.

Please note that although this seems to do the trick, some other problem may stand in your way. It is generally not a good idea to override existing class methods in a category since the original implementation becomes inaccessible after that. Documentation on the NSKeyValueObserving informal protocol states that

NSObject provides an implementation of the NSKeyValueObserving protocol that provides an automatic observing capability for all objects.

So if you override observeValueForKeyPath:ofObject:change:context: in a category, the default implementation will be inaccessible (and we cannot be sure that UITableView or UIScrollView do not user KVO for something). That may cause some unexpected errors.

Community
  • 1
  • 1
Egor Chiglintsev
  • 1,252
  • 10
  • 9
  • How if I implemention `observeValueForKeyPath:ofObject:change:context:` in some Class 'Foo', and UITableView category keep a instance of Foo? – OpenThread Feb 12 '13 at 15:36
  • 1
    Implementing `observeValueForKeyPath:ofObject:change:context:` in a separate Foo class should indeed help with that 'overriding existing method in a category' issue. – Egor Chiglintsev Feb 12 '13 at 15:46
8

Egor Chiglintsev's answer reminded me I can observe the panGestureRecognizer property already exposed in UIScrollView. It should be much safer than pan. But then.. I found out I can just add myself as a target!

[_scrollView.panGestureRecognizer addTarget:self action:@selector(gestureRecognizerUpdate:)];

This works great for me!

Mazyod
  • 22,319
  • 10
  • 92
  • 157
  • 1
    I guess that's the proper way it should be really done. Don't know why the first things coming in my mind are usually some runtime or KVO-related hacks. Thanks) – Egor Chiglintsev Jun 30 '14 at 09:17