我已经为 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]
是一种将文本视图返回到正确高度,并重置滚动视图内容大小,以及确保所有其他子视图正确定位的方法)。
请勿调整文本视图的框架。您所需要做的就是滚动滚动视图。如果你滚动滚动视图动画,那么文本视图将向上滑动动画。只需执行以下操作:
[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];
} ];
我相信我也有类似的经历。我希望我的水平 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 中的高速公路),并将其优先于其他事物!
希望这对未来的读者有所帮助!
我也有类似的问题。基本上我有一个滚动视图,包含三个页面,每个页面包含一个自定义
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;
不会发生这种重新定位,并且滚动按预期工作。
完全破解:可能会改善重置问题:
- (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView
{
[self setContentOffset:self.contentOffset animated:NO];
}
如果直接在 contentSize 中分配一些值,contentOffset 将被重置 (0, 0)。 相反,通过使用 contentLayoutGuide 来告诉scrollView contentSize 是怎样的。
NSLayoutConstraint.activate([
textView.leadingAnchor.constraint(equalTo: scrollView.contentLayoutGuide.leadingAnchor),
textView.trailingAnchor.constraint(equalTo: scrollView.contentLayoutGuide.trailingAnchor),
textView.topAnchor.constraint(equalTo: scrollView.contentLayoutGuide.topAnchor),
textView.bottomAnchor.constraint(equalTo: scrollView.contentLayoutGuide.bottomAnchor)
])