UIKit:当子视图的宽度增加超过屏幕边缘时,UIScrollView 自动滚动
在 iPhone 的上下文中:
我有一个包含 UIImage
的 UIScrollView
。当用户点击 UIImage
内的屏幕时,会在用户触摸的位置添加一个 UITextField
。用户可以编辑此 UITextField
,并且文本字段将根据文本是否添加或删除自动调整自身大小。
当正在编辑的 UITextField
增加其宽度时,滚动视图会自动滚动以显示增加的宽度。
问题的出现是因为文本字段的自动滚动不考虑屏幕的 y 值
例如,假设用户在图像底部添加了一个文本字段。当他们编辑该文本字段时,键盘将显示,隐藏文本字段。我有代码可以滚动屏幕以显示文本字段。当用户输入太多文本以致文本字段超出屏幕边缘时,问题就出现了。发生这种情况时,屏幕会水平滚动以适应更宽的文本,但也会垂直滚动 - 垂直滚动最终会隐藏文本字段,基本上使我为显示文本字段所做的任何操作都无效。
如果文本字段被键盘隐藏则显示文本字段的代码:
- (void)keyboardWasShown:(NSNotification*)notification
{
NSDictionary* info = [notification userInfo];
CGSize keyboardSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
self.offset = self.contentOffset;
CGRect frame = self.frame;
// self.activeField is the name of the field that is the current first responder - this just adds a little bit of padding
frame.size.height -= keyboardSize.height + (self.activeField.frame.size.height * 2);
if (!CGRectContainsPoint(frame, self.activeField.frame.origin)) {
CGPoint scrollPoint = CGPointMake(self.offset.x, self.activeField.frame.origin.y - keyboardSize.height + (activeField.frame.size.height * 2));
[self setContentOffset:scrollPoint animated:YES];
}
}
这是增加文本字段大小的代码:
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
NSString *newString = [textField.text stringByReplacingCharactersInRange:range withString:string];
CGSize stringSize = [string sizeWithFont:textField.font];
CGSize newSize = [newString sizeWithFont:textField.font];
// Make textField wider if we're close to running up against it
if (newSize.width > (textField.frame.size.width - self.widthOffset)) {
CGRect textFieldFrame = textField.frame;
if (stringSize.width > self.widthOffset) {
textFieldFrame.size.width += stringSize.width;
}
textFieldFrame.size.width += self.widthOffset;
textField.frame = textFieldFrame;
}
// Shrink the textField if there is too much wasted space
if ((textField.frame.size.width - newSize.width) > self.widthOffset) {
CGRect textFieldFrame = textField.frame;
textFieldFrame.size.width = newSize.width + self.widthOffset;
textField.frame = textFieldFrame;
}
return YES;
}
问题是:如何让 UIScrollView
尊重 y -自动滚动时自身的值?
In the context of the iPhone:
I have a UIScrollView
containing a UIImage
. When the user taps the screen inside the UIImage
, a UITextField
is added where the user touched. The user can edit this UITextField
, and the text field will automatically resize itself based on whether text was added or deleted.
When the a UITextField
being edited increases its width, the scrollview automatically scrolls to show the increased width.
The problem comes in because the automatic scrolling of the textfield doesn't respect the y-value of the screen
For example, say the user added a text field to the bottom of the image. When they go to edit that text field, the keyboard will show, hiding the text field. I have code in place to scroll the screen to show the text field. The problem comes in when the user enters so much text that that text field extends past the edge of the screen. When this happens, the screen scrolls horizontally to fit the wider text, but also vertically - the vertical scrolling ends up hiding the textfield, basically nullifying anything I did to show the text field.
Code to show the text field if it's hidden by the keyboard:
- (void)keyboardWasShown:(NSNotification*)notification
{
NSDictionary* info = [notification userInfo];
CGSize keyboardSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
self.offset = self.contentOffset;
CGRect frame = self.frame;
// self.activeField is the name of the field that is the current first responder - this just adds a little bit of padding
frame.size.height -= keyboardSize.height + (self.activeField.frame.size.height * 2);
if (!CGRectContainsPoint(frame, self.activeField.frame.origin)) {
CGPoint scrollPoint = CGPointMake(self.offset.x, self.activeField.frame.origin.y - keyboardSize.height + (activeField.frame.size.height * 2));
[self setContentOffset:scrollPoint animated:YES];
}
}
Here is the code to increase the size of the text field:
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
NSString *newString = [textField.text stringByReplacingCharactersInRange:range withString:string];
CGSize stringSize = [string sizeWithFont:textField.font];
CGSize newSize = [newString sizeWithFont:textField.font];
// Make textField wider if we're close to running up against it
if (newSize.width > (textField.frame.size.width - self.widthOffset)) {
CGRect textFieldFrame = textField.frame;
if (stringSize.width > self.widthOffset) {
textFieldFrame.size.width += stringSize.width;
}
textFieldFrame.size.width += self.widthOffset;
textField.frame = textFieldFrame;
}
// Shrink the textField if there is too much wasted space
if ((textField.frame.size.width - newSize.width) > self.widthOffset) {
CGRect textFieldFrame = textField.frame;
textFieldFrame.size.width = newSize.width + self.widthOffset;
textField.frame = textFieldFrame;
}
return YES;
}
The question is: How do I get the UIScrollView
to respect the y-value of itself when automatically scrolling?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
基本上
UIScrollView
的setFrame
将重新调整滚动视图offset
,由_adjustContentOffsetIfNecessary
完成。由于该方法是私有的并且没有记录,因此我们几乎无法猜测调整将如何发生。有两种方法可以阻止不必要的滚动或设置错误的
offset
:1) 在应用
setFrameUIScrollView
offset
代码>.如果您根据某些计算有意修改UIScrollView
的框架,则可以执行此操作。2) 应用
offset
更改而不使用动画。在您的keyboardWasShown
中,更改[self setContentOffset:scrollPoint 动画:YES];到
[self setContentOffset:scrollPointanimated:NO];
原因:当在动画开启的情况下应用多个
offset
时,结果offset
是不明确的。这里的内部方法(_adjustContentOffsetIfNecessary
)应用偏移量更改,另一个由您的代码完成。如果您尝试记录UIScrollView
委托方法中应用的所有偏移量,您会注意到这一点:如果这有帮助,请告诉我。
Basically
setFrame
ofUIScrollView
will readjust the scrollviewoffset
, done by_adjustContentOffsetIfNecessary
. As that method is private and not documented, there is very little we can guess on how the adjustment will happen.There are two ways to stop the unnecessary scrolling or wrong
offset
being set:1) reset the
UIScrollView
offset
after applyingsetFrame
. This you can do, if you are modifying the frame ofUIScrollView
intentionally based on some calculations.2) apply
offset
changes without animation. In yourkeyboardWasShown
, change[self setContentOffset:scrollPoint animated:YES]; to
[self setContentOffset:scrollPoint animated:NO];
Reason: when more than one
offset
is applied with animation on, the resultoffset
is ambiguous. Here the internal method(_adjustContentOffsetIfNecessary
) applies an offset change and the other one is done by your code. You can notice this if you try to log all the offsets being applied in theUIScrollView
delegate method:Let me know if this helps.
一种可能的解决方法是响应
scrollViewDidScroll
委托方法检查以查看UITextField
是否再次隐藏,然后在必要时重新滚动。看起来有点像黑客,但听起来UIScrollView
自动滚动行为是阻碍你的事情,如果没有办法直接影响它,唯一的选择就是解决它。然而,也有一个缺点,如果你这样做,那么它看起来会滚动两次。如果仅当
UITextField
扩展到屏幕边缘之外时才会发生自动滚动行为,如果该字段看起来要扩展到屏幕边缘之外,您也可以移动该字段以保持完全可见。One possible workaround could be to respond to the
scrollViewDidScroll
delegate method check to see if theUITextField
is hidden again, then re-scroll if necessary. Seems like a bit of a hack, but it sounds like theUIScrollView
auto scrolling behavior is what's getting in your way, and if there's no way to directly affect it, the only option is to work around it. There is also the disadvantage, however, that if you do this then it appears to scroll twice.If the auto-scrolling behavior happens only when the
UITextField
expands beyond the edge of the screen, you could also move the field to stay completely visible if it looks like it's going to expand beyond the edge of the screen.不要更改内容偏移量,而是更改滚动视图的显示框架。
如果您不允许用户更改屏幕方向,则 fullSizedFrame 可以设置为视图首次显示时的原始框架。如果允许更改方向,您需要根据方向计算 fullSizedFrame 的适当值。
Instead of changing the content offset, change the scroll view's display frame.
If you don't allow the user to change screen orientation, then fullSizedFrame could be set to the original frame of the view when it is first displayed. If changes in orientation are allowed, you'll need to calculate the appropriate value for fullSizedFrame based on the orientation.