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 的内容偏移被重置的主要内容,如果未能解决你的问题,请参考以下文章