web-dev-qa-db-ja.com

UITextFieldがファーストレスポンダーになったときにUIScrollViewのスクロールを無効にする

UITextFieldに埋め込まれたUIScrollViewが最初のレスポンダーになると、ユーザーが何らかの文字を入力すると、UIScrollViewはそのフィールドに自動的にスクロールし、それを無効にしますか?

重複rdar:// 16538222以上

60
Rabih

モシェの答えに基づいて...

UIScrollViewをサブクラス化し、次のメソッドをオーバーライドします。

- (void)scrollRectToVisible:(CGRect)rect animated:(BOOL)animated

空のままにします。仕事完了!

61
Luke

私は同じ問題に苦労してきましたが、ようやく解決策を見つけました。

コールトレースを追跡することで自動スクロールがどのように行われるかを調査しましたが、内部[UIFieldEditor scrollSelectionToVisible]は、UITextFieldに文字が入力されると呼び出されます。このメソッドは、UIScrollViewの最も近い先祖のUITextFieldに作用するようです。

したがって、textFieldDidBeginEditingで、UITextFieldを同じサイズの新しいUIScrollViewでラップする(つまり、UITextFieldの間にビューを挿入する)スーパービューです)、これは自動スクロールをブロックします。最後に、textFieldDidEndEditingのこのラッパーを削除します。

コードは次のようになります。

- (void)textFieldDidBeginEditing:(UITextField*)textField {  
    UIScrollView *wrap = [[[UIScrollView alloc] initWithFrame:textField.frame] autorelease];  
    [textField.superview addSubview:wrap];  
    [textField setFrame:CGRectMake(0, 0, textField.frame.size.width, textField.frame.size.height)]; 
    [wrap addSubview: textField];  
}  

- (void)textFieldDidEndEditing:(UITextField*)textField {  
  UIScrollView *wrap = (UIScrollView *)textField.superview;  
  [textField setFrame:CGRectMake(wrap.frame.Origin.x, wrap.frame.Origin.y, wrap.frame.size.width, textField.frame.size.height)];
  [wrap.superview addSubview:textField];  
  [wrap removeFromSuperview];  
}  

お役に立てれば!

56
Taketo Sano

UITextViewのセルであるUITableViewの自動スクロールを無効にすると、同じ問題が発生しました。次のアプローチを使用して解決できました。

@interface MyTableViewController : UITableViewController<UITextViewDelegate>

@implementation MyTableViewController {
    BOOL preventScrolling;
    // ...
}

// ... set self as the delegate of the text view

- (void)textViewDidBeginEditing:(UITextView *)textView {
    preventScrolling = YES;
}

- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
    if (preventScrolling) {
        [self.tableView setContentOffset:CGPointMake(0, -self.tableView.contentInset.top) animated:NO];
    }
}

- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
    preventScrolling = NO;
}

scrollViewWillBeginDraggingの定義は、ユーザー自身がスクロールを開始したときに、デフォルトのスクロール動作を復元するために使用されます。

8
tonso

Taketoが述べたように、UITextFieldがファーストレスポンダーになると、UIScrollView型(存在する場合)の最初の親ビューがスクロールされて、そのUITextFieldが表示されます。最も簡単なハックは、各UITextFieldをUIScrollViewにラップすることです(理想的には、それらすべてを単一のダミーUIScrollViewにラップすることです)。これはTaketoのソリューションと非常によく似ていますが、パフォーマンスがわずかに向上するはずで、私の意見ではコード(またはInterface Builderのインターフェイス)がずっときれいになります。

6
danbretl

Lukeの答えに基づいて、彼のソリューションが自動スクロールを完全に無効にするという問題を処理するには、次のように選択的に無効にすることができます。

//  TextFieldScrollView
#import <UIKit/UIKit.h>

@interface TextFieldScrollView : UIScrollView

@property (assign, nonatomic) IBInspectable BOOL preventAutoScroll;

@end

@implementation TextFieldScrollView

- (void)scrollRectToVisible:(CGRect)rect animated:(BOOL)animated {
    if (self.preventAutoScroll == NO) {
        [super scrollRectToVisible:rect animated:animated];
    }
}

@end

これにより、Interface Builderで完全に設定して自動スクロールを無効にすることができますが、いつでも完全に制御して再度有効にすることができます(ただし、なぜあなたは私を超えたいのでしょう)。

3
mbm29414

UITextfieldを含むUIScrollviewのように見え、コンテンツオフセットを自動調整します。テキストフィールドがファーストレスポンダーになるとき。これは、最初に同じサイズのスクロールビューにテキストフィールドを追加してから、メインのスクロールビューに追加することで解決できます。メインスクロールビューに直接追加する代わりに

    // Swift

    let rect = CGRect(x: 0, y: 0, width: 200, height: 50)

    let txtfld = UITextField()
    txtfld.frame = CGRect(x: 0, y: 0, width: rect.width, height: rect.height)

    let txtFieldContainerScrollView = UIScrollView()
    txtFieldContainerScrollView.frame = rect
    txtFieldContainerScrollView.addSubview(txtfld)
    // Now add this txtFieldContainerScrollView in desired UITableViewCell, UISCrollView.. etc
    self.mainScrollView.addSubview(txtFieldContainerScrollView)

    // Am33T
2
Amit Tandel

これは私がそれを行う方法です:

それは非常に簡単で、任意のscrollRectToVisibleに対して独自のcontentOffsetを返すことができます。

これにより、通常の動作や物の流れを損なうことなく、同じチャンネルで同じ機能を提供するだけで、独自の改善が得られます。

#import <UIKit/UIKit.h>

@protocol ExtendedScrollViewDelegate <NSObject>

- (CGPoint)scrollView:(UIScrollView*)scrollView offsetForScrollingToVisibleRect:(CGRect)rect;

@end

@interface ExtendedScrollView : UIScrollView

@property (nonatomic, unsafe_unretained) id<ExtendedScrollViewDelegate> scrollToVisibleDelegate;

@end

#import "ExtendedScrollView.h"

@implementation ExtendedScrollView

- (void)scrollRectToVisible:(CGRect)rect animated:(BOOL)animated
{
    if (_scrollToVisibleDelegate && [_scrollToVisibleDelegate respondsToSelector:@selector(scrollView:offsetForScrollingToVisibleRect:)])
    {
        [self setContentOffset:[_scrollToVisibleDelegate scrollView:self offsetForScrollingToVisibleRect:rect] animated:animated];
    }
    else
    {
        [super scrollRectToVisible:rect animated:animated];
    }
}

@end
1
daniel.gindi

コレクションビューの一番上にテキストフィールドがあり、UITableView.tableHeaderView。このテキストフィールドは、コレクションビューの残りの部分に干渉しないように、負のコンテンツオフセットスペースに配置されます。私は基本的に、ユーザーがスクロールビューでスクロールを実行しているかどうか、テキストフィールドがファーストレスポンダーであるかどうか、スクロールビューがスクロールビューのコンテンツインセットの上部を超えてスクロールされているかどうかを検出しています。この正確なコードは、必ずしも誰にも役立つわけではありませんが、ケースに合わせて操作できます。

- (void)scrollViewDidScroll:(UIScrollView *)scrollView {

    // This is solving the issue where making the text field first responder
    // automatically scrolls the scrollview down by the height of the search bar.
    if (!scrollView.isDragging && !scrollView.isDecelerating &&
        self.searchField.isFirstResponder &&
        (scrollView.contentOffset.y < -scrollView.contentInset.top)) {

        [scrollView setContentOffset:CGPointMake(scrollView.contentOffset.x, -scrollView.contentInset.top) animated:NO];
    }
}
0
TPoschel

@TaketoSanoの答えを試してみましたが、うまくいかないようです。

そして最後に、回避策を得ました。キーボードに必要な2つのデフォルトの通知名があります。

  • UIKeyboardDidShowNotificationキーボードが表示されたとき。
  • UIKeyboardWillHideNotificationキーボードが非表示になるとき。

私が使用したサンプルコードは次のとおりです。

- (void)viewDidLoad {
  [super viewDidLoad];

  ...

  NSNotificationCenter * notificationCetner = [NSNotificationCenter defaultCenter];
  [notificationCetner addObserver:self
                         selector:@selector(_keyboardWasShown:)
                             name:UIKeyboardDidShowNotification
                           object:nil];
  [notificationCetner addObserver:self
                         selector:@selector(_keyboardWillHide:)
                             name:UIKeyboardWillHideNotification
                           object:nil];
}

- (void)_keyboardWasShown:(NSNotification *)note {
  [self.view setFrame:(CGRect){{272.f, 55.f}, {480.f, 315.f}}];
}

- (void)_keyboardWillHide:(NSNotification *)note {
  [self.view setFrame:(CGRect){{272.f, 226.5f}, {480.f, 315.f}}];
}

ここでは、(CGRect){{272.f, 226.5f}, {480.f, 315.f}}は、キーボードが非表示のときのビューのデフォルトフレームです。そして(CGRect){{272.f, 55.f}, {480.f, 315.f}}は、キーボードが表示されたときのビューのフレームです。

そして、b.t.w。、ビューのフレーム変更は自動的にアニメーションに適用されます、これは本当に完璧です!

0
Kjuly

それを許可するUIScrollViewのプロパティは知りません。私見では、それを無効にできるのはユーザーエクスペリエンスの低下でしょう。

ただし、スクロールする前に、UIScrollViewをサブクラス化し、そのメソッドの一部をオーバーライドして、UITextfieldがファーストレスポンダーではないことを確認することができます。

0
Moshe