UIScrollView 的内容偏移被重置

Posted

技术标签:

【中文标题】UIScrollView 的内容偏移被重置【英文标题】:UIScrollView's content offset being reset 【发布时间】:2012-02-14 03:40:47 【问题描述】:

我一直在为 UIScrollView 的问题苦苦挣扎。

我有一个UIScrollVIew,其中包含一个UITextView 作为子视图。当我选择文本视图时,会弹出一个键盘。我想调整文本视图的大小以完全适合可用空间,并滚动滚动视图以使文本视图准确定位在可见空间中(不被键盘隐藏)。

当键盘出现时,我调用一个方法来计算文本视图的适当大小,然后执行以下代码:

[UIView animateWithDuration:0.3 
                 animations:^
  
     self.textView.frame = frame;
   
 ];

[self.scrollView setContentOffset:CGPointMake(0,frame.origin.y) animated:YES];

(这里的框架是文本视图的适当框架)。

不幸的是,滚动视图并不总是滚动到正确的位置,尤其是当我选择文本视图时它已经处于非零垂直内容偏移量时。我知道我设置的内容偏移是正确的。

经过大量的测试,我终于意识到发生了什么是动画完成后,滚动视图又自动滚动了。

此代码确实有效:

UIView animateWithDuration:0.3 
                 animations:^
  
     self.textView.frame = frame;
 
 completion:^(BOOL finished) 
     [self.scrollView setContentOffset:CGPointMake(0, frame.origin.y) animated:YES];
 
 ];

但它看起来很奇怪,因为滚动视图滚动到错误的位置,然后滚动到正确的位置。

有谁知道当文本视图框架完成其动画时,我可以如何防止滚动视图更改其内容偏移量?

我正在使用 ios 5.0 进行测试。


这是我发现可行的解决方案。我仍然不完全了解发生了什么,可能与我的弹簧和支柱的设置方式有关。基本上,我将滚动视图内容的大小缩小了与文本视图缩小的量相同。

- (void)keyboardDidShow:(NSNotification*)aNotification

    NSDictionary* info = [aNotification userInfo];

    // Get the height of the keyboard
    CGRect kbRect = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue];
    kbRect = [self.view convertRect:kbRect fromView:nil];
    CGSize kbSize = kbRect.size;

    // Adjust the height of the text view to fit in the visible view
    CGRect frame = self.textView.frame;
    int visibleHeight = self.view.frame.size.height;
    visibleHeight -= kbSize.height;
    frame.size.height = visibleHeight;

    // Get the new scroll view content size
    CGSize contentSize = self.scrollView.contentSize;
    contentSize.height = contentSize.height - self.textView.frame.size.height + frame.size.height;

    [UIView animateWithDuration:0.1 
                     animations:^
      
         self.textView.frame = frame;
         // Note that the scroll view content size needs to be reset, or the scroll view
         // may automatically scroll to a new position after the animation is complete.
         self.scrollView.contentSize = contentSize;
         [self.scrollView setContentOffset:CGPointMake(0, frame.origin.y) animated:YES];
          
     ];

    // Turn off scrolling in scroll view
    self.scrollView.scrollEnabled = NO;


// Called when the UIKeyboardWillHideNotification is sent
- (void)keyboardWillBeHidden:(NSNotification*)aNotification

    // Update the view layout
    [UIView animateWithDuration:0.3 animations:^
     
         self.scrollView.contentOffset = CGPointZero;
         [self updateViewLayout];
     
     ];

    // Turn on scrolling in the scroll view
    self.scrollView.scrollEnabled = YES;

[self updateViewLayout] 是一种将文本视图返回到正确高度、重置滚动视图内容大小以及确保所有其他子视图正确定位的方法。

【问题讨论】:

【参考方案1】:

不要调整文本视图的框架。您需要做的就是滚动滚动视图。如果您滚动滚动视图动画,则 textView 将动画向上滑动。只需执行以下操作:

[self.scrollView setContentOffset:CGPointMake(0, frame.origin.y) animated:YES];

如果您需要滚动视图以特定速率滚动,然后手动调整动画块中的内容偏移量。(我没有测试过,您可能需要调整动画标志,或者逐步调整内容偏移量如果这不起作用,则一次像素)

[UIView animateWithDuration:0.3f 
             animations:^ 
  [self.scrollView setContentOffset:CGPointMake(0, frame.origin.y) animated:YES];
 ];

【讨论】:

感谢您的回复。但是我确实需要调整我的文本视图的框架。我没有改变原点,而是文本视图框架的高度,一旦键盘出现,它太大而无法容纳在可用的屏幕空间中。 如果你不调整文本视图框架的原点,那么你应该能够改变它的高度,然后滚动滚动视图。听起来您正在调整帧原点。 CGRect frame = CGRectMake(self.textView.frame.origin.x,self.textView.frame.origin.y,self.textView.frame.size.width,self.textView.frame.size.height-50.0f); 'self.textView.frame = frame; 是的,我正在做与您的代码示例类似的事情,它不应该改变原点。问题似乎是当动画完成时,滚动视图会自动滚动到一个新位置。所以除非我等到动画完成来设置 contentOffset,否则滚动视图最终会出现在错误的位置。 也许如果你能发布更多你的实现,我可以告诉你到底出了什么问题。如果您不希望滚动视图调整其内容偏移量,请不要调用[self.scrollView setContentOffset:CGPointMake(0, frame.origin.y) animated:YES]; 如果您想滚动滚动视图并同时调整框架,请将它们都放在动画块中。 [UIView animateWithDuration:0.3f animations:^ [self.scrollView setContentOffset:CGPointMake(0, frame.origin.y) animated:YES]; self.textView.frame = frame; ]; 感谢您的有用建议。不过,这对我不起作用。整个问题是动画完成后,滚动视图再次自动滚动。我实际上已经找到了解决方案。我更新动画块中的滚动内容大小,将其缩小与文本视图缩小的量相同,并且自动滚动停止。这可能与我的弹簧和支柱的设置方式有关。我将在问题中发布更多我的代码。【参考方案2】:

我相信我也有类似的东西。我希望我的水平 UIScrollView 回到它的原始偏移量(0,0),每次我动画回到它时,它只会偏离几个像素,但是一旦有人触摸滚动视图,它就会继续并完成动画(几乎就像电子游戏中的滞后)。

因此,我尝试将它作为主队列放入 GCD 中,之后它运行得非常好。代码如下:

- (void)setPageZero
       dispatch_async(dispatch_get_main_queue(), ^
       [UIView animateWithDuration:1.0f
                delay:0 options:UIViewAnimationOptionCurveEaseInOut
                animations:^
                     [self.scrollView setContentOffset:CGPointMake(0, self.scrollView.frame.origin.y) animated:YES];
                 
           completion:^(BOOL finished) ];
       );

它的基本作用是将动画放入主队列(有点像 CPU/GPU 中的高速公路),并将其优先于其他事物!

希望这对未来的读者有所帮助!

【讨论】:

这不起作用。使用动画设置 contentOffset 会忽略动画块。如果要在块中设置动画,则必须使用 self.scrollView.contentOffset = CGPointMake(0,0);将其包裹在动画块中基本上是没有意义的,它不起作用。注意:如果您使用动画块,则不会调用 scrollView 上的某些委托方法,例如didScroll:【参考方案3】:

我遇到了类似的问题。基本上我有一个包含三个页面的滚动视图,每个页面都包含一个自定义UIView 类。自定义UIView 类包含一个图像和许多包含图像信息的覆盖视图。覆盖视图包含许多UITextView 字段。

我将自定义视图附加到滚动视图,然后使用块操作加载图像和叠加层。我通过删除第一个视图、重新定位接下来的两个视图并在右侧添加一个新视图来向右滑动。

如果我创建 UITextField 对象并禁用滚动如下:

UITextView* view = [[UITextView alloc]
                    initWithFrame:CGRectMake(position.x,position.y,width,height)];
view.editable = NO;  
view.scrollEnabled = NO;

然后,当我从块代码返回时,滚动视图会在页面之间重新定位。请注意,我没有重新定位滚动视图,并且在退出块时,滚动视图处于正确位置。

但是,如果我注释掉禁用滚动的代码

UITextView* view = [[UITextView alloc]
                    initWithFrame:CGRectMake(position.x,position.y,width,height)];
view.editable = NO;  
// view.scrollEnabled = NO;

这种重新定位不会发生,并且滚动按预期工作。

【讨论】:

【参考方案4】:

完全破解:可能会改善重置问题:

- (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView
        
        [self setContentOffset:self.contentOffset animated:NO];

【讨论】:

以上是关于UIScrollView 的内容偏移被重置的主要内容,如果未能解决你的问题,请参考以下文章

UIScrollView 最大和最小内容偏移量?

使用自定义 UIControl 自动调整 UIScrollView 内容偏移

IOS 管理键盘 - 上移 UIScrollView 内容

UIScrollview 动画取决于内容偏移

UIScrollView 不滚动偏移内容

在触摸期间停止 UIScrollView 滚动