滚动快速后,粘滞滚动视图子视图的帧不正确。可编辑的例子

时间:2013-12-18 05:42:08

标签: ios objective-c iphone uiview uiscrollview

过去12个小时我一直在为此而努力,而且我正在进行脑力激荡。

  

关于bitbucket的观点:   https://bitbucket.org/burneraccount/scrollviewexample

     

git https:https://bitbucket.org/burneraccount/scrollviewexample.git

     

git ssh:git@bitbucket.org:burneraccount/scrollviewexample.git

^这是一个简洁,独立的Xcode项目,它体现了我在实际工作中遇到的问题。

摘要

我正在尝试实现静态图像效果,其中图像(示例中的摇滚)出现在屏幕上,而较低的内容向上滚动似乎超出了滚动视图。

屏幕大小为(UIScrollView*)mainScrollView。此滚动视图有(UIView*)contentViewcontentView长约1200个,有两个子视图:(UIScrollView *)imageScroller和(UITextView *)textView。

one

一切顺利,直到你向上滚动一点,然后快速向下滚动。移动停止后产生的位置不正确。它在某种程度上无法在scrollviewDidScroll委托方法中正确更新。快速向下滚动(仅在您向上拖动之后)表现得有些不正确,但仍会导致错误放置视图:

two

轻轻拖动几乎什么也没做,滚动的速度与不正确的偏移直接相关。

以下是有问题的代码:

- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
    if (scrollView == self.mainScrollView)
    {

        CGFloat mainOffset = self.mainScrollView.contentOffset.y;
        if (mainOffset > 0 && mainOffset < self.initialImageScrollerFrame.size.height)
        {
            CGRect imageFrame = self.imageScroller.frame;
            CGFloat offset = mainOffset-self.lastOffset;
            self.imageScroller.frame = CGRectMake(imageFrame.origin.x,
                                                  imageFrame.origin.y + offset, // scroll up
                                                  imageFrame.size.width,
                                                  imageFrame.size.height - offset); // hide the bottom of the image
            self.lastOffset = mainOffset;
        }

    }
}

我几乎以我能想象的方式对其进行了重组,它总是有类似的效果。最糟糕的部分是非常简单直接的代码无法完成看起来像是保证做的事情。 同样奇怪的是,我在我的真实项目中使用的缩放效果使用相同的机制来调整相同的视图元素。它在(contentOffset < 0)内而不是(contentOffset > 0)内运行,因此当您将视图拉低到正常偏移量时,它只会放大imageScroller。这导致我认为有些数据在穿过contentOffset 0时会丢失,但我的阴谋一整天都被击落了。

这个示例比我正在处理的真实项目复杂得多,但它正确地再现了问题。如果您无法打开我的项目,或者无法连接到回购,请告诉我。

对此可能有一个很明显的解决办法,但我已经过了几个小时,有了新的观点。如果我能让它发挥作用,我将深感惊讶。

4 个答案:

答案 0 :(得分:2)

- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
    if (scrollView == self.mainScrollView)
    {

        CGFloat mainOffset = self.mainScrollView.contentOffset.y;
        if (mainOffset >= 0 && mainOffset < self.initialImageScrollerFrame.size.height)
        {
            CGRect imageFrame = self.imageScroller.frame;
            CGFloat offset = self.mainScrollView.contentOffset.y-self.lastOffset;
            if ( imageFrame.origin.y + offset > 0) {  //[2]
                self.imageScroller.frame = CGRectMake(imageFrame.origin.x,
                                                      imageFrame.origin.y + offset, 
                                                      imageFrame.size.width,
                                                      imageFrame.size.height - offset); 
                self.lastOffset = mainOffset;
            }
        }else if (mainOffset < 0) {  //[1]
            self.imageScroller.frame = self.initialImageScrollerFrame;
        }
    }
}

[1] - 当您的mainOffset低于零时,您需要重置imageScroller框架。 ScrollViewDidScroll没有注册每个像素的移动值,它只是经常被调用(尝试记录它,你会看到)。因此,当您滚动得更快时,它的偏移量会进一步分开。在下次调用scrollViewDiDScroll时,这可能会导致说+15的正滚动成为负滚动。所以你的imageScroller.frame.y可能会卡在+15偏移量上,即使你期望它为零。因此,只要你检测到mainOffset的负值,就需要重置你的imageScroller帧。

[2] - 同样在这里你需要确保你的imageScroller.frame.y总是+ ve。

这些修复可以完成这项工作,但我仍然认为它们“丑陋且不值得生产”。为了更清洁的方法,您可能需要重新考虑这些视图之间的相互作用,以及您要实现的目标。

顺便说一句,你的'image.png'实际上是一个jpg。虽然这些在模拟器上渲染得很好,但它会在设备上中断(有编译器错误)。

答案 1 :(得分:0)

我会在这里发布我的更新。

一位回答者提及- (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView虽然这里不适用,但类似的scrollViewDidEndDecelerating:却有效。在我昨天的挣扎中,我实施了

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
    if (self.mainScrollView.contentOffset.y < 10)
    {
        [UIView animateWithDuration:0.2 animations:^{
            self.imageScroller.frame = self.initialImageScrollerFrame;

        }];
    }
}

这有点像一种解决方法,但它很难看,绝对不值得生产。如果contentOffset足够接近0,它只是将帧动画回原始位置。这是向正确方向迈出的一步,但我担心它不值得作为解决方案。

<强> ----

还添加了

- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView
                     withVelocity:(CGPoint)velocity
              targetContentOffset:(inout CGPoint *)targetContentOffset
{
    CGPoint point = *targetContentOffset;
    CGFloat offset = point.y;

    if (0 <= offset && offset < 10)
    {
        [UIView animateWithDuration:0.2 animations:^{
            self.imageScroller.frame = self.initialImageScrollerFrame;
        }];
    }
}

这使我更接近预期的效果,但仍然无法用于生产应用程序。

答案 2 :(得分:0)

同样的问题但是为了添加另一个功能,左/右列表UIScrollView的按钮,快速点击它后,滚动视图打破了我的大脑;(

我认为更好的滚动没有动画,它可以防止UIScrollView内容出现故障。可能不是答案,但是......

答案 3 :(得分:-1)

Hey mate使用下面的方法来帮助你,它是UIScrollView的委托方法:

- (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView

在此方法中,您可以将scrollview框架更改为x和y为0,0