崩溃 - 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 以消除一些并发症。
情况:
我已经将 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 以消除一些并发症。