崩溃 - UITableViewCell 子类 KVO'ing UITableView.panGestureRecogonizer.state

Crash - UITableViewCell subclass KVO'ing UITableView.panGestureRecogonizer.state

情况:

我已经将 UITableViewCell 子类化,因为我需要在 UITableViewCell 的任一侧添加自定义操作按钮。在某些情况下,我需要将 UITableView 设置回正常状态(隐藏自定义操作按钮)。 例如 当用户在 UITableView 中向上滚动时。为此,我将我的自定义 UITableViewCell 添加为包含 UITableView 的 UIPangestureRecognizer 状态的观察者。

问题:

弹出包含 UITableView 和自定义 UITableViewCells 的 UIViewController 时,我收到以下错误:

*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'An instance 0x7b21b920 of class UIScrollViewPanGestureRecognizer was deallocated while key value observers were still registered with it. Current observation info: ( Context: 0xb83618, Property: 0x7b3e13b0> Context: 0xb83618, Property: 0x7b3e13b0> Context: 0xb83618, Property: 0x7b3e13b0> Context: 0xb83618, Property: 0x7b3e13b0> )'

这显然是说 UIPanGestureRecognizer 在自定义 UITableViewCell 之前被释放。

问题:

我应该在哪里删除自定义 UITableViewCell 作为 UITableView 的 UIPanGestureRecognizer 的观察者,这样我就不会遇到这个异常?

代码: (我希望这不是太多需要梳理的代码。如果是的话,我深表歉意。)

CustomUITableViewCell.m

#pragma mark - Setter Methods

- (void)setContainingTableView:(UITableView *)containingTableView
{
    if (self.isObservingContainingTableViewPanGestureRecognizer)
    {
        self.observingContainingTableViewPanGestureRecognizer = NO;

        [_containingTableView.panGestureRecognizer removeObserver:self forKeyPath:kUITableViewPanGestureRecognizerStateKeyPath];
    }

    _containingTableView = containingTableView;

    if (containingTableView)
    {
        self.observingContainingTableViewPanGestureRecognizer = YES;

        [containingTableView.panGestureRecognizer addObserver:self forKeyPath:kUITableViewPanGestureRecognizerStateKeyPath options:0 context:UITableViewPanGestureRecogonizerContext];
    }
}

#pragma mark -


#pragma mark - Overrides

- (void)didMoveToSuperview
{
    [super didMoveToSuperview];

    self.containingTableView = nil;

    UIView * view = self.superview;

    while (view)
    {
        if ([view isKindOfClass:[UITableView class]])
        {
            self.containingTableView = (UITableView *)view;
            break;
        }

        view = view.superview;
    }
}

- (void)dealloc
{
    self.containingTableView = nil;
}

#pragma mark -

#pragma mark - Key Value Observing

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
    if (context == UITableViewPanGestureRecogonizerContext)
    {
        if ([keyPath isEqual:kUITableViewPanGestureRecognizerStateKeyPath])
        {
            UIPanGestureRecognizer * panGestureRecognizer = (UIPanGestureRecognizer *)object;

            if (panGestureRecognizer.state == UIGestureRecognizerStateBegan)
            {
                CGPoint velocity = [panGestureRecognizer velocityInView:self.contentCellView];

                if (fabs(velocity.y) >= fabs(velocity.x))
                {
                    [self.scrollView setContentOffset:CGPointZero animated:YES];
                }
            }
        }
    }
    else {
        [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
    }
}

#pragma mark - 

一如既往,非常感谢您的帮助!另外,如果有人需要更多信息,请告诉我。

编辑:

奇怪的是,自定义 UITableViewCell 的 dealloc 方法被调用,并且在抛出异常之前自定义 UITableViewCell 作为观察者被删除。

UITableView 是 UIScrollView 的子类。如果您只想检测用户何时滚动它,您可以使用 scrollview 委托方法:

  • (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView;

我认为您应该将 table 视图的弱引用传递给 cellForRowAtIndexPath 中的单元格(作为委托),而不是在 didMoveToSuperview 中查找 table 视图。总的来说,我认为在单元格中设置 table 视图的手势观察器不是一个好主意。但是,如果您真的想要它,请确保 register/unregister 正确。 还要确保 isObservingContainingTableViewPanGestureRecognizer 标志的初始值在单元格被重用时是正确的。

事实证明,我需要保留对 UITableView 的 UIPanGestureRecognizer 的引用。我很可能最终会子类化 UITableView 以消除一些并发症。