禁用UITableView中UITableViewCell内UITextField编辑时的自动滚动

55
我正在使用自定义的UITableViewCell在我的UITableView中。每个UITableViewCell都很高,并且顶部包含一个UITextField
当用户点击UITextField以编辑它时,键盘会出现,并且UITableView会自动滚动,以便该单元格位于屏幕顶部。
问题是,这会将UITableView滚动到UITableViewCell的底部,而不是顶部。当UITableViewCell很高并编辑UITextField在顶部时,您无法看到UITextField。我知道如何通过编程方式滚动UITableView,但我不知道如何禁用此自动滚动,以便我可以自己滚动UITableView。我该怎么做?

你解决了吗?我也遇到了同样的问题。 - Steven Baughman
@SteveBaughman 你找到解决方案了吗? - HighFlyer
不行:(实际上,我因为这个和其他一些原因停止使用表格视图了...:/) - animal_chin
这绝对是UITableViewController自动行为的问题。 - Kudit
9个回答

73

autoscroll-behavior位于UITableViewController功能中。

要禁用自动滚动,我找到了两种方法:

  1. 使用UIViewController代替UITableViewController - 自己设置数据源和委托。
  2. 重写viewWillAppear方法并不调用[super viewWillAppear: animated]

这两种解决方案都会禁用自动滚动,但还会禁用一些其他不是必需的但很好用的功能,这些功能在苹果类参考文档的概述中有描述:

https://developer.apple.com/documentation/uikit/uitableviewcontroller


3
我遇到了同样的问题,方案2可行。我已经在iOS 5.0.0及以上版本上测试过,目前还有效(当前版本为6.1.2)。虽然我无法保证它是否适用于4,但嘿,现在是2013年,我们可以说5.0是最低支持的操作系统了。 - Cocoadelica
问题在于插图没有适应键盘,至少在iOS 7.0.6中不是这样。这意味着需要更多的代码手动完成此操作:捕获键盘通知...很丑陋。 - meaning-matters
第二点在9.1版本上有效。@Dominic给出了很好的答案。谢谢。 - pkc456
9
在不调用super的情况下覆盖viewWillAppear:是不可取的。UITableViewController是UIViewController的子类,苹果公司“要求”所有UIViewController的子类都必须调用super。根据UIViewController文档所述:“如果您覆盖此方法,则必须在实现中某个时刻调用super。” - ryanipete
我正在使用 UITableViewController,如果我不使用 supper.viewWillAppear() 函数,那么我的页脚就无法出现在键盘的顶部。我该怎么做才能让页脚视图停止自动滚动,并在编辑时将其放置在键盘的顶部? - Pushpendra
显示剩余2条评论

8

为你的 UITableViewController 定义属性:

@property (nonatomic) BOOL scrollDisabled;
@property (nonatomic) CGFloat lastContentOffsetY;

调用becomeFirstResponder之前:
// Save the table view's y content offset 
lastContentOffsetY = tableViewController.tableView.contentOffset.y;
// Enable scrollDisabled
scrollDisabled = YES;

将以下代码添加到您的表视图控制器中:

-(void)scrollViewDidScroll:(UIScrollView *)scrollView
{
    if (self.scrollDisabled) {
        [self.tableView setContentOffset:CGPointMake(0, lastContentOffsetY)];
    }
    ...
}

在调用resignFirstResponder之后,设置scrollDisabled = NO

这不是一个ScrollView的问题,而是TableViewController类的问题。正确的答案是重写viewWillAppear方法。 - Sophy Swicz

5
您可以执行以下操作:
- (void)registerForKeyboardNotifications
{
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(keyboardWillShow:)
                                                 name:UIKeyboardWillShowNotification object:nil];

    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(keyboardDidShow:)
                                                 name:UIKeyboardDidShowNotification object:nil];
}

- (void)unregisterForKeyboardNotifications
{
    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardDidShowNotification object:nil];
}

- (void)keyboardWillShow:(NSNotification *)notification
{
    self.tableView.scrollEnabled = NO;
}

- (void)keyboardDidShow:(NSNotification *)notification
{
    double delayInSeconds = 0.3;
    dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
    dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
            self.tableView.scrollEnabled = YES;
    });
}

那么实现这个UIScrollViewDelegate方法。
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
    if (! self.tableView.scrollEnabled)
        [self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForItem:0 inSection:0] atScrollPosition:UITableViewScrollPositionTop animated:NO];
}

但是请注意,如果用户点击的UITextField位置会被键盘覆盖,则它将不会滚动。

在我看来,最好的做法是确保从顶部到包括UITextField的那个单元格,在键盘显示时都可见。


-scrollViewDidScroll: 在自动滚动发生时确实会被调用,这里是最好的解决方案。 - Nikolay Spassov

4

最好的方法是子类化 UITableView,然后覆盖 setContentOffset(_ contentOffset: CGPoint, animated: Bool) 方法,不要调用 super.setContentOffset(_ contentOffset: CGPoint, animated: Bool)。在这个方法中,视图控制器正在执行自动滚动。

override func setContentOffset(_ contentOffset: CGPoint, animated: Bool)
{
    //Do nothing
}

这对于我来说是UITableView中带有UITextView的正确答案。 - Olle Lind

3
您可以通过以下方式禁用自动内容插入调整:
tableView.contentInsetAdjustmentBehavior = .never

1
这样做行不通,因为键盘会遮挡在它下面的单元格。 - Felix Marianayagam

2

对我来说问题不在于它的滚动,而是编辑文本视图被移出屏幕。

因此,我并没有阻止滚动,而是在编辑触发时将表格视图重新滚动到我想要的位置,像这样:

public func textViewShouldBeginEditing(textView: UITextView) -> Bool {            
  dispatch_async(dispatch_get_main_queue()) {
    tableViewController.tableView.scrollToRowAtIndexPath(self.indexPath!, atScrollPosition: UITableViewScrollPosition.Middle, animated: true)
  }
  return true
}

1

很遗憾,在iOS 8中覆盖-viewWillAppear:对我无效。

这是我的解决方案(如在UITableViewController实现中):

- (void)viewDidLoad {

[super viewDidLoad];

[[NSNotificationCenter defaultCenter] removeObserver:self.tableView name:UIKeyboardWillShowNotification object:nil];

[[NSNotificationCenter defaultCenter] removeObserver:self.tableView name:UIKeyboardWillHideNotification object:nil];
}

由于自动滚动行为是通过UIKeyboard的显示/隐藏通知来调用的,因此不要观察它们。

这个对你还有效吗?在iOS 11 / Xcode 9中无法使其工作。 - simonthumper

-2
你是否尝试将“scrollsToTop”这个属性设置为NO,它是tableview的一个属性,默认值为YES。

还没试过,但是不起作用...scrollToTop不能处理这个...无论如何,谢谢回答! - animal_chin

-6
你可以尝试以下操作:
self.tableView.scrollEnabled = NO;

这应该会禁用表格视图中的滚动视图。


1
这并不会阻止UITableViewController在单击UITableViewCell中的文本字段时执行自动滚动。 - Kudit

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接