【问题标题】:Syncing the scroll position of multiple UITableView instances同步多个 UITableView 实例的滚动位置
【发布时间】:2012-03-26 20:16:33
【问题描述】:

我有一个项目,我需要在 iPad 上的同一视图中显示多个 UITableView 实例。它们也恰好是旋转的,但我相当肯定这无关紧要。用户应该不知道视图是由多个表视图组成的。因此,我想让它在我滚动一个 tableview 时,其他的同时滚动。

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

Here's a sample project I created.

如何在不继承 UITableView 的情况下实现此行为?

【问题讨论】:

    标签: objective-c ios uitableview uikit


    【解决方案1】:

    您可以通过直接观察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];
    }
    

    这可能会变得更漂亮,但它可以传达想法。

    【讨论】:

    • 哇哦!它完美地工作。我完全忘记了键值观察。谢谢。
    • 你不能用一个单独的“ScrollMultiplexer”对象来做这个,所以你没有讨厌的 n^2 观察吗?一个对象可以观察所有 UITableView,并且该对象可以向所有其他 UITableView 发送更新。这样,就没有循环,也不需要不断地脱钩。
    • 嗯,这已经发生在一个“单独的”对象(视图控制器)上,所以对我来说它是如何工作的并不是很明显,但我可以承认这种可能性。
    • 别忘了,在返回按钮下添加[table removeObserver:self forKeyPath:@"contentOffset"];
    【解决方案2】:

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

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

    【讨论】:

      【解决方案3】:

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

      1. 将自己标记为 UITableViewDelegate:

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

        override func viewDidLoad() {
            super.viewDidLoad()
        
            leftTableView.delegate = self
            rightTableView.delegate = self
        }
        

        (您也可以在界面生成器中执行此步骤)。

      3. 实现这个方法:

        func scrollViewDidScroll(scrollView: UIScrollView) {
            leftTableView.contentOffset = scrollView.contentOffset
            rightTableView.contentOffset = scrollView.contentOffset
        }
        

      完成。

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

      【讨论】:

        猜你喜欢
        • 2010-10-20
        • 2010-12-22
        • 1970-01-01
        • 2011-04-26
        • 2012-03-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多