【问题标题】:Can I override scroll view's automatic behavior to scroll to the first responder?我可以覆盖滚动视图的自动行为以滚动到第一响应者吗?
【发布时间】:2011-11-30 08:42:56
【问题描述】:

我在 UIScrollView 中有一个 UITextField(深几级)。我正在观看 UIKeyboardDidShowNotification,并且当我手动更改第一响应者时也会调用相同的代码(我可能会更改为不同的文本字段而不会暂时隐藏键盘)。在该代码中,我使用scrollRectToVisible:animated: 来确保 UITextField 是可见的。

我非常头疼调试为什么这很有趣,但我现在意识到 UIScrollView 会自动确保第一响应者在其范围内。我正在更改 UIScrollView 的框架,以便它没有隐藏在键盘后面。

但是,我的代码可能比他们的代码更智能,因为我不仅想显示 UITextField,还想显示一些附近的相关视图。如果它们合适,我会尝试展示这些观点;如果不是什么,我会尽可能多地显示它们,但至少要确保 UITextField 是可见的。所以我想保留我的自定义代码。

自动行为干扰了我的代码。我看到的是滚动视图轻轻向上滚动,以便我的内容的底部边缘可见,然后它会向下对齐到我的代码告诉它定位的位置。

有没有办法阻止 UIScrollView 执行其将第一响应者滚动到视图中的默认功能?

更多信息

在查看文档时,我读到他们建议更改滚动视图的 contentInset 而不是框架。我改变了它并消除了一些不可预测的行为,但它并没有解决这个特定的问题。

我认为发布所有代码不一定有用。但这是当时的关键调用和重要属性的值。我只会为 CGRects 编写 4 元组;我的意思是(x,y,宽度,高度)。

[scrollView scrollRectToVisible:(116.2, 71.2, 60, 243) animated:YES];

scrollView.bounds == (0, 12, 320, 361)

scrollView.contentInset == UIEdgeInsetsMake(0, 0, 118, 0)

textField.frame == (112.2, 222.6, 24, 24)

转换为 scrollView 的直接子视图的坐标 == (134.2, 244.6, 24, 24)

转换为scrollView的坐标== (134.2, 244.6, 24, 24)

因此,由于插入,滚动视图底部边缘实际上位于 y == 243。

请求的矩形扩展到 y == 314.2。

文本字段扩展到 y == 268.6。

两者都超出范围。 scrollRectToVisible 正在尝试解决其中一个问题。标准的 UIScrollView / UITextField 行为试图修复另一个。他们没有提出完全相同的解决方案。

【问题讨论】:

  • 对于它的价值,我让默认行为适用,它根本不会打扰我。自动滚动的结果与我的代码实现的结果相差不大。
  • 目前不确定这是否重要,但我评论了下面的一个很好的解决方案。在键盘显示处理程序中调用您自己的 scrollRectToVisible:animated: 之前的零秒延迟似乎“覆盖”了该行为。

标签: iphone uiscrollview first-responder


【解决方案1】:

我没有测试这种特殊情况,但我设法通过子类化滚动视图并覆盖 setContentOffset: 和 setContentOffset:animated: 来防止滚动视图在顶部和底部弹跳。滚动视图在每次滚动移动时都会调用它,所以我相当肯定它们会在滚动到文本字段时被调用。

您可以使用委托方法 textFieldDidBeginEditing: 来确定何时允许滚动。

在代码中:

- (void)textFieldDidBeginEditing:(UITextField *)textField
{
    self.blockingTextViewScroll = YES;
}

-(void)setContentOffset:(CGPoint)contentOffset
{
    if(self.blockingTextViewScroll)
    {
        self.blockingTextViewScroll = NO;
    }
    else
    {
        [super setContentOffset:contentOffset];
    }
}


-(void)setContentOffset:(CGPoint)contentOffset animated:(BOOL)animated
{
    if(self.blockingTextViewScroll)
    {
        self.blockingTextViewScroll = NO;
    }
    else
    {
        [super setContentOffset:contentOffset animated:animated];
    }
}

如果您当前的滚动行为适用于 setContentOffset: 覆盖,只需将其放在 else 块内(或者最好放在从 else 块调用的方法中)。

【讨论】:

  • 这行不通,因为函数的调用顺序不符合此要求。第一次点击 textField setContentOffset 时,首先调用 blockingTextViewScroll 直到之后才设置为 true。该解决方案仅在此之后才起作用,因为 blockingTextViewScroll 将是 true 但它会导致意外行为。
【解决方案2】:

在我的项目中,我通过仅在延迟一段时间后执行滚动来成功实现了这一目标。

- (void)keyboardWillShow:(NSNotification *)note
{
    NSDictionary *userInfo = note.userInfo;
    CGRect keyboardFrame = [[userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];

    UIEdgeInsets contentInsets = self.tableView.contentInset;
    contentInsets.bottom += keyboardFrame.size.height;

    [self.tableView setContentInset:contentInsets];
     [self performSelector:@selector(scrollToEditableCell) withObject:nil afterDelay:0];
}

还有其他可能使您的视图与其他视图成为第一响应者和傻瓜滚动视图滚动到哪里。尚未对此进行测试。

【讨论】:

  • 看起来这可能已被否决,但这是一个不错的解决方案。我发现了同样的情况:在执行 scrollRectToVisible 之前的零延迟将使这项工作,因为它在滚动视图的自动行为之后安排它。使用 GCD 的 dispatch_after 比 performSelector:WithObject:afterDelay 更干净、更现代。
【解决方案3】:

这可能没有用,但您是否尝试在调用 scrollrectToVisible: 之前将 scrollView.userInteractionEnabled 设置为 NO,然后将其设置回 YES?它可能会阻止自动滚动行为。

【讨论】:

  • 不。为了彻底起见,我尝试了在滚动视图和 UITextField 上禁用 userInteractionEnabled 的所有组合,并将这些调用放置在 scrollRectToVisible 或 becomeFirstResponder 之前和之后,或者两者之前和之后。在 UITextField 上禁用 userInteractionEnabled 通常会使键盘不出现,即使在我调用 becomeFirstResponder 时启用它也是如此。在滚动视图上设置它似乎没有任何效果。
【解决方案4】:

尝试将视图自动调整大小更改为UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleRightMargin。默认是FlexibleTopMargin 所以也许这就是原因。顺便说一句,scrollRectToVisible: 正在使用scrollView.contentSize

您可以尝试先更改scrollView 大小,然后应用scrollRectToVisible: 更改。先换帧,再换内容。 (可能观察到键盘确实出现了事件)

【讨论】:

  • "尝试更改视图自动调整大小" 哪个视图?滚动视图?还有什么?我可能不会去那里。我必须将所有自动调整大小的蒙版设置为非常具体的设置,以使所有滚动、缩放和倾斜都能正常工作。 “另一件事你可以尝试先改变scrollView的大小,然后再应用scrollRectToVisible:改变。”做到了。
  • 如果您对自动调整大小有所了解,也许您可​​以查看my other question
【解决方案5】:

从 iOS 14 开始,自动滚动行为似乎特别有问题。我通过子类化 UIScrollView 并覆盖 setContentOffset 来缓解这个问题。这是我的代码的基础。

class ManualScrollView: UIScrollView {

    /// Use this function to set the content offset. This will forward the call to
    /// super.setContentOffset(:animated:)
    /// - Parameters:
    ///   - contentOffset: A point (expressed in points) that is offset from the content view’s origin.
    ///   - animated: true to animate the transition at a constant velocity to the new offset, false to make the transition immediate.
    func forceContentOffset(_ contentOffset: CGPoint, animated: Bool) {
        super.setContentOffset(contentOffset, animated: animated)
    }

    /// This function has be overriden to do nothing to block system calls from changing the
    /// content offset at undesireable times.
    ///
    /// Instead call forceContentOffset(:animated:)
    override func setContentOffset(_ contentOffset: CGPoint, animated: Bool) {

    }
}

这可行,但您必须重新实现许多通常免费获得的滚动视图行为和方法。由于scrollRectToViewscrollToView 都使用setContentOffset,如果你想让它们工作,你还必须重新实现它们。

【讨论】:

    猜你喜欢
    • 2021-04-25
    • 1970-01-01
    • 1970-01-01
    • 2012-10-14
    • 2012-10-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多