web-dev-qa-db-ja.com

UITableViewCell内のUITextFieldを編集するときにUITableViewの自動スクロールを無効にする

UITableViewCell内でカスタムUITableViewsを使用しています。これらのUITableViewCellsはそれぞれ非常に高く、上部にUITextFieldが含まれています。

ユーザーがUITextFieldを編集するためにタップすると、キーボードが表示され、UITableViewが自動的にスクロールして、セルが画面の上部に表示されます。

問題は、これによりUITableViewが上部ではなくUITableViewCellの下部にスクロールされることです。 UITableViewCellが高く編集されている場合、UITextFieldが一番上にあるため、UITextFieldが見えません。 UITableViewをプログラムでスクロールする方法は知っていますが、UITableViewを自分でスクロールできるように、この自動スクロールを無効にする方法がわかりません。これどうやってするの?

51
animal_chin

Autoscroll-behaviorはUITableViewController機能にあります。

自動スクロールを無効にするには、2つの方法を見つけました。

  1. UITableViewControllerの代わりに単にUIViewControllerを使用します-データソースを設定し、自分で委任します。
  2. viewWillAppearメソッドをオーバーライドし、[super viewWillAppear: animated]を呼び出さない

どちらのソリューションでも、Autoscrollだけでなく、Appleのクラスリファレンスの概要で説明されているその他のNiceではあるが必須ではない機能も無効にします。

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

66
Dominic Sander

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;

Table View Controllerに次のコードを追加します。

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

resignFirstResponderを呼び出した後、scrollDisabled = NO

8
william-yang

次のことができます。

- (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が含まれるセルまでのすべてのセルが表示されるようにすることです。

4
arturgrigor

私にとっての問題は、スクロールするほどではなく、編集中のテキストビューが画面から外れることでした。

したがって、スクロールを防止する代わりに、次のように、編集がトリガーされたときに目的の場所にTableViewを再スクロールするだけです:

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
infinite-loop

残念ながら、-viewWillAppear:のオーバーライドはiOS 8では機能しません。

ここに私のソリューションがあります(UITableViewController実装のように):

- (void)viewDidLoad {

[super viewDidLoad];

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

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

自動スクロール動作はUIKeyboardの表示/非表示通知によって呼び出されるため、それらを監視しないでください。

1
Ke Yang

最良の方法は、UITableViewをサブクラス化してからsetContentOffset(_ contentOffset: CGPoint, animated: Bool)をオーバーライドし、super.setContentOffset(_ contentOffset: CGPoint, animated: Bool)を呼び出さないことです。この方法では、View Controllerが自動スクロールを実行します。

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