UIKit:当子视图的宽度增加超过屏幕边缘时,UIScrollView 自动滚动

发布于 2024-11-13 05:58:25 字数 2575 浏览 0 评论 0原文

在 iPhone 的上下文中:

我有一个包含 UIImageUIScrollView 。当用户点击 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

撩心不撩汉 2024-11-20 05:58:25

基本上 UIScrollViewsetFrame 将重新调整滚动视图 offset,由 _adjustContentOffsetIfNecessary 完成。由于该方法是私有的并且没有记录,因此我们几乎无法猜测调整将如何发生。
有两种方法可以阻止不必要的滚动或设置错误的 offset

1) 在应用 setFrameUIScrollView offset代码>.如果您根据某些计算有意修改 UIScrollView 的框架,则可以执行此操作。

CGPoint oldOffset = [scrollView contentOffset];         
scrollView.frame = CGRectMake(newFrame);
[scrollView setContentOffset:oldOffset];        

2) 应用 offset 更改而不使用动画。在您的 keyboardWasShown 中,更改
[self setContentOffset:scrollPoint 动画:YES];到
[self setContentOffset:scrollPointanimated:NO];

原因:当在动画开启的情况下应用多个offset时,结果offset 是不明确的。这里的内部方法(_adjustContentOffsetIfNecessary)应用偏移量更改,另一个由您的代码完成。如果您尝试记录 UIScrollView 委托方法中应用的所有偏移量,您会注意到这一点:

-(void)scrollViewDidScroll:(UIScrollView *)scrollView
{
    NSLog(@" offset: %@", NSStringFromCGPoint(scrollView.conentOffset))
}

如果这有帮助,请告诉我。

Basically setFrame of UIScrollView will readjust the scrollview offset, 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 applying setFrame. This you can do, if you are modifying the frame of UIScrollView intentionally based on some calculations.

CGPoint oldOffset = [scrollView contentOffset];         
scrollView.frame = CGRectMake(newFrame);
[scrollView setContentOffset:oldOffset];        

2) apply offset changes without animation. In your keyboardWasShown , change
[self setContentOffset:scrollPoint animated:YES]; to
[self setContentOffset:scrollPoint animated:NO];

Reason: when more than one offset is applied with animation on, the result offset 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 the UIScrollView delegate method:

-(void)scrollViewDidScroll:(UIScrollView *)scrollView
{
    NSLog(@" offset: %@", NSStringFromCGPoint(scrollView.conentOffset))
}

Let me know if this helps.

嗳卜坏 2024-11-20 05:58:25

一种可能的解决方法是响应 scrollViewDidScroll 委托方法检查以查看 UITextField 是否再次隐藏,然后在必要时重新滚动。看起来有点像黑客,但听起来 UIScrollView 自动滚动行为是阻碍你的事情,如果没有办法直接影响它,唯一的选择就是解决它。然而,也有一个缺点,如果你这样做,那么它看起来会滚动两次。

如果仅当 UITextField 扩展到屏幕边缘之外时才会发生自动滚动行为,如果该字段看起来要扩展到屏幕边缘之外,您也可以移动该字段以保持完全可见。

One possible workaround could be to respond to the scrollViewDidScroll delegate method check to see if the UITextField is hidden again, then re-scroll if necessary. Seems like a bit of a hack, but it sounds like the UIScrollView 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.

惜醉颜 2024-11-20 05:58:25

不要更改内容偏移量,而是更改滚动视图的显示框架。

- (void)keyboardWill:(NSNotification*)notification
{
    CGSize keyboardSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
    // Update the scroll view's frame to the region not covered by the keyboard.
    self.frame = CGRectMake(self.fullSizedFrame.origin.x, 
                            self.fullSizedFrame.origin.y, 
                            self.fullSizedFrame.size.width, 
                            self.fullSizedFrame.size.height - keyboardSize.height);
}

- (void)keyboardWillHide:(NSNotification*)notification
{
    // Set the frame back to the original frame before the keyboard was shown.
    self.frame = self.fullSizedFrame;
}

如果您不允许用户更改屏幕方向,则 fullSizedFrame 可以设置为视图首次显示时的原始框架。如果允许更改方向,您需要根据方向计算 fullSizedFrame 的适当值。

Instead of changing the content offset, change the scroll view's display frame.

- (void)keyboardWill:(NSNotification*)notification
{
    CGSize keyboardSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
    // Update the scroll view's frame to the region not covered by the keyboard.
    self.frame = CGRectMake(self.fullSizedFrame.origin.x, 
                            self.fullSizedFrame.origin.y, 
                            self.fullSizedFrame.size.width, 
                            self.fullSizedFrame.size.height - keyboardSize.height);
}

- (void)keyboardWillHide:(NSNotification*)notification
{
    // Set the frame back to the original frame before the keyboard was shown.
    self.frame = self.fullSizedFrame;
}

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文