【问题标题】:Setting contentOffset programmatically triggers scrollViewDidScroll以编程方式设置 contentOffset 触发 scrollViewDidScroll
【发布时间】:2012-03-14 04:03:14
【问题描述】:

我在一个页面上有几个UIScrollView。您可以单独滚动它们或将它们锁定在一起并将它们作为一个滚动。当它们被锁定时会出现问题。

我使用UIScrollViewDelegatescrollViewDidScroll: 来跟踪运动。我查询 UIScrollViewcontentOffset 发生了变化,然后通过设置它们的 contentOffset 属性来匹配其他滚动视图的变化。

太好了....除了我注意到很多额外的电话。以编程方式更改我的滚动视图的contentOffset 会触发调用委托方法scrollViewDidScroll:。我尝试改用setContentOffset:animated:,但我仍然在委托上获得触发器。

如何以编程方式修改我的 contentOffsets 以不触发 scrollViewDidScroll:

实施说明.... 每个UIScrollView 都是自定义UIView 的一部分,该自定义UIView 使用委托模式回调呈现的UIViewController 子类,该子类处理各种contentOffset 值的协调。

【问题讨论】:

  • 我有同样的问题,我在 UITableView 中使用 UITextView,当文本视图调整大小时,触发 UITableView->scrollViewDidScroll ;-(
  • Tarc 的回答完美。从那时起,我开始了解修改 bounds 属性并了解正在发生的事情。

标签: ios uiscrollview uiscrollviewdelegate


【解决方案1】:

可以在不触发委托回调scrollViewDidScroll: 的情况下更改UIScrollView 的内容偏移量,方法是将UIScrollView 的边界设置为所需的内容偏移量。

CGRect scrollBounds = scrollView.bounds;
scrollBounds.origin = desiredContentOffset;
scrollView.bounds = scrollBounds;

【讨论】:

  • 我的男人为什么这么警惕?它是所有内容偏移量都是真的,并且可以使用 UIView 动画块和东西进行动画处理。
  • 这仍然会再次调用scrollViewDidScroll,但直到当前scrollViewDidScroll函数结束后才会这样做。
  • @Mark -- 我没有看到这个排队呼叫scrollViewDidScroll。在某些情况下会发生这种情况吗?
【解决方案2】:

这不是问题的直接答案,但如果您收到看似虚假的此类消息,也可能是因为您正在更改界限。我正在使用一些带有“tilePages”方法的Apple示例代码,该方法可以删除子视图并将其添加到滚动视图。这很少会导致立即调用额外的 scrollViewDidScroll: 消息,因此您会陷入您肯定没想到的递归。就我而言,我遇到了令人讨厌的无法找到崩溃。

我最终做的是在主队列中排队:

- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
    if(scrollView == yourScrollView) {
        // dispatch fixes some recursive call to scrollViewDidScroll in tilePages (related to removeFromSuperView)
        // The reason can be found here: http://stackoverflow.com/questions/9418311
        dispatch_async(dispatch_get_main_queue(), ^{ [self tilePages]; });
    }
}

【讨论】:

  • 有趣,虽然在这种情况下不是我的问题,但我会记住这一点。
【解决方案3】:

试试

id scrollDelegate = scrollView.delegate;
scrollView.delegate = nil;
scrollView.contentOffset = point;
scrollView.delegate = scrollDelegate;

为我工作。

【讨论】:

    【解决方案4】:

    另一种方法是在您的 scrollViewDidScroll 委托中添加一些逻辑,以确定内容偏移量的更改是通过编程方式触发还是由用户触摸触发。

    • 向您的类添加一个“isManualScroll”布尔变量。
    • 将其初始值设置为 false。
    • 在 scrollViewWillBeginDragging 中将其设置为 true。
    • 在您的 scrollViewDidScroll 中检查它是否为真,如果是则仅响应。
    • 在 scrollViewDidEndDecelerating 中将其设置为 false。
    • 如果速度为 0,则在 scrollViewWillEndDragging 添加逻辑以将其设置为 false(因为在这种情况下不会调用 scrollViewDidEndDecelerating)。

    【讨论】:

    • 最佳答案在这里。不过有一个修改:我会使用scrollViewDidEndDragging(_:willDecelerate:) 而不是scrollViewWillEndDragging。然后检查速度是否为 0,而不是检查 decelerate 是否为假,这会明确告诉您 scrollViewWillEndDecelerating 不会被调用。
    【解决方案5】:

    如何使用 UIScrollView 的现有属性?

    func scrollViewDidScroll(_ scrollView: UIScrollView) {
        if (scrollView.isTracking || scrollView.isDragging || scrollView.isDecelerating) {
            /// The content offset was changed programmatically.
            /// Your code goes here.
        }
    }
    

    【讨论】:

    • 可能,但很脆弱。如果UIScrollView API 通过添加新的类似属性而发生更改,它会导致您的代码中断。您还必须担心这些标志消失的标志。毕竟,我正在检查“确实发生了变化”,并且滚动视图可能不再具有标志,因为操作已完成。
    • @DBD - 如果您担心 UIKit 中的每个 API 都会发生变化,我不知道您将如何完成任何事情。依赖公开记录的 API 并不脆弱。
    • @Shaheen - 我并不是说该解决方案是由旋转玻璃制成的,但我发现它对未来的证明比预期的要少。解决方案的前提是检查 3 个潜在活动,如果不是这些活动,则通过消除过程假设它是第 4 个。我关心的不只是 Apple 只是更改了一个公共 API,而是 Apple 向 API 添加了功能,这需要逻辑更新才能使代码继续运行,Apple 确实定期对 UIKit 进行添加,两者都作为新类以及对现有课程的补充。如果你不喜欢我的理由也没关系。
    • @DBD - 我同意你的观点,“解决方案的前提是检查 3 个潜在的活动,如果不是这些活动,则通过消除过程假设它是第 4 个”,这不是理想的逻辑。
    • 迄今为止最好的答案。与边界解决方案不同,它不是什么神秘的副作用,绝对会随着未来的行为而改变,并且滚动视图引入新概念的可能性非常低,除非引入新的触摸交互类型——让我们面对现实吧,10 年进入iOS(我们现在甚至通过了强制触摸)是极不可能发生的。如果确实如此……大呼,在您的代码中再添加一个交互,您就完成了。甚至可以将它旋转成一个扩展(带有“ieUserInteracting”属性)并一次性处理所有滚动视图。完成。
    【解决方案6】:

    简化@Tark 的答案,您可以在不触发scrollViewDidScroll 的情况下定位滚动视图,如下所示:

    scrollView.bounds.origin = CGPoint(x:0, y:100); // whatever values you'd like
    

    【讨论】:

    • 不确定发生了什么,但在 Xcode 9 中 - 为 iOS 11 构建,即使设置边界,scrollViewDidScroll 似乎也会触发。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-07-29
    • 1970-01-01
    • 2018-05-05
    • 2019-04-04
    • 1970-01-01
    相关资源
    最近更新 更多