关于iphone:UIKit:当子视图增加其宽度超过屏幕边缘时,UIScrollView会自动滚动

UIKit: UIScrollView automatically scrolling when a subview increases its width past the edge of the screen

就iPhone而言:

我有一个UIScrollView,里面有一个UIImage。当用户点击UIImage内的屏幕时,用户触摸的地方会添加一个UITextField。用户可以编辑此UITextField,文本字段将根据添加或删除的文本自动调整自身大小。

当正在编辑的a UITextField增加其宽度时,滚动视图自动滚动以显示增加的宽度。

问题出现是因为文本字段的自动滚动不尊重屏幕的Y值。

例如,假设用户在图像底部添加了一个文本字段。当他们编辑文本字段时,键盘将显示,隐藏文本字段。我有代码可以滚动屏幕显示文本字段。当用户输入的文本太多以至于文本字段超出屏幕边缘时,问题就出现了。当发生这种情况时,屏幕水平滚动以适应更宽的文本,但也垂直滚动-垂直滚动最终隐藏了文本字段,基本上取消了我显示文本字段所做的任何操作。

如果文本字段被键盘隐藏,则显示该字段的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
- (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];
    }
}

下面是增加文本字段大小的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
- (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值?


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

1)应用setFrame后,复位UIScrollViewoffset。如果您有意根据某些计算修改UIScrollView的框架,则可以这样做。

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

2)无动画应用offset更改。在你的keyboardWasShown中,更改[self setContentOffset:scrollPoint animated:YES]; to
[self setContentOffset:scrollPoint animated:NO];

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

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

如果有帮助,请告诉我。


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

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


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
- (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计算适当的值。