Objective c 同步多个UITableView实例的滚动位置

Objective c 同步多个UITableView实例的滚动位置,objective-c,ios,uitableview,uikit,Objective C,Ios,Uitableview,Uikit,我有一个项目,其中我需要在iPad的同一视图中显示多个UITableView实例。它们也碰巧是旋转的,但我相当确定这是不相关的。用户应该不知道视图是由多个表视图组成的。因此,我希望这样,当我滚动一个tableview时,其他的tableview会同时滚动 因为UITableView是UIScrollView的一个子类,我想我可以处理UIScrollViewDelegate方法并将它们传递给所有的tableview。不幸的是,虽然我可以捕获一些事件,但方法调用粒度不够精细,并且我无法将这些消息传递

我有一个项目,其中我需要在iPad的同一视图中显示多个
UITableView
实例。它们也碰巧是旋转的,但我相当确定这是不相关的。用户应该不知道视图是由多个表视图组成的。因此,我希望这样,当我滚动一个tableview时,其他的tableview会同时滚动

因为
UITableView
UIScrollView
的一个子类,我想我可以处理
UIScrollViewDelegate
方法并将它们传递给所有的tableview。不幸的是,虽然我可以捕获一些事件,但方法调用粒度不够精细,并且我无法将这些消息传递给其他TableView。我能得到的最接近的方法是实现
-scrollViewDidScroll:
,然后在每个tableview上调用
-setContentOffset:animated
。如果我尝试为所有可能的情况发送此消息,我最终会被锁定,因为有人调用我的
-setContentOffset:animated
调用,所以我最终会被冻结。不管怎样,如果我仅通过使用此方法检测一个tableview上的滚动并将其传递给其他tableview来消除锁定,我会发现,虽然其他tableview最终会滚动到相同的位置,但它们会落后一秒或两秒


如何在不子类化
UITableView
的情况下实现此行为?

您可以通过直接观察
contentOffset
绕过回调粒度问题。在
viewDidLoad
中,在需要同步的每个表视图上设置KVO:

for(UITableView *view in self.tableViewCollection)
{
    [view addObserver:self 
           forKeyPath:@"contentOffset" 
              options:NSKeyValueObservingOptionNew 
              context:NULL];
}
然后,在观察更改时,暂时取消捕捉观察,更新其他表视图的偏移,然后重新启用观察

- (void)observeValueForKeyPath:(NSString *)keyPath 
                      ofObject:(id)object 
                        change:(NSDictionary *)change 
                       context:(void *)context
{
    static BOOL isObservingContentOffsetChange = NO;
    if([object isKindOfClass:[UITableView class]] 
       && [keyPath isEqualToString:@"contentOffset"])
    {
        if(isObservingContentOffsetChange) return;

        isObservingContentOffsetChange = YES;
        for(UITableView *view in self.tableViewCollection)
        {
            if(view != object)
            {
                CGPoint offset = 
                 [[change valueForKey:NSKeyValueChangeNewKey] CGPointValue];
                view.contentOffset = offset;
            }
        }
        isObservingContentOffsetChange = NO;
        return;
    }

    [super observeValueForKeyPath:keyPath 
                         ofObject:object 
                           change:change 
                          context:context];
}

这可能会变得更漂亮,但它让人明白了这一点。

直接使用
contentOffset
属性,而不是
setContentOffset:animated
,以避免延迟。在您的示例中,将第73行和第74行更改为

self.tableViewMiddle.contentOffset = scrollView.contentOffset;
self.tableViewBottom.contentOffset = scrollView.contentOffset;

老问题,但这里有一个更简单的方法

  • 将自己标记为UITableViewDelegate:

    class MultiTableViewController: UIViewController, UITableViewDelegate { ... }
    
  • 将自己设置为表视图的代理:

    override func viewDidLoad() {
        super.viewDidLoad()
    
        leftTableView.delegate = self
        rightTableView.delegate = self
    }
    
    (也可以在interface builder中执行此步骤)

  • 实施此方法:

    func scrollViewDidScroll(scrollView: UIScrollView) {
        leftTableView.contentOffset = scrollView.contentOffset
        rightTableView.contentOffset = scrollView.contentOffset
    }
    
  • 完成了


    表视图有一个滚动视图,UITableViewDelegate协议包含所有UIScrollViewDelegate方法,因此您可以直接使用它们。

    Woohoo!它工作得很好。我完全忘记了观察关键值。谢谢。你不能用一个单独的“ScrollMultiplexer”对象来完成这项工作,这样你就不会有令人讨厌的n^2观察结果了吗?一个对象可以观察所有UITableView,该对象可以向所有其他UITableView发送更新。这样,就没有循环,也不需要不断地取消挂钩。好吧,这已经发生在一个“单独”的对象(视图控制器)上,所以我不知道该如何工作,但我可以承认这种可能性。别忘了,在后退按钮下添加[table removeObserver:self forKeyPath:@“contentOffset”];