Отключить прокрутку UIScrollView, когда UITextField становится первым ответчиком

Когда UITextField, встроенный в UIScrollView, становится первым ответчиком, например, если пользователь вводит какой-то символ, UIScrollView автоматически прокручивается до этого поля, есть ли способ отключить его?

Дубликат rdar://16538222 над https://bugreport.apple.com

Ответ 1

Основываясь на ответе Моше...

Подкласс UIScrollView и переопределить следующий метод:

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

Оставьте его пустым. Задание выполнено!

Ответ 2

Я боролся с той же проблемой, и наконец нашел решение.

Я исследовал, как выполняется автоматический прокрутка, отслеживая трассировку вызова, и обнаружил, что внутренний [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];  
}  

надеюсь, что это поможет!

Ответ 3

У меня была та же проблема с отключением автоматической прокрутки 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 используется для восстановления поведения прокрутки по умолчанию, когда сам пользователь инициирует прокрутку.

Ответ 4

Как упоминал Такето, когда UITextField создается первым ответчиком, его первое родительское представление типа UIScrollView (если оно существует) прокручивается, чтобы сделать видимым UITextField. Самый простой взлом - просто обернуть каждый UITextField в UIScrollView (или, в идеале, обернуть все их в один манекен UIScrollView). Это очень похоже на решение Taketo, но оно должно дать вам немного лучшую производительность, и я буду держать ваш код (или ваш интерфейс в Interface Builder) более чистым, на мой взгляд.

Ответ 5

Похоже, UIScrollview, который содержит UITextfield, автоматически настраивает смещение содержимого; когда текстовое поле станет первым ответчиком. Это можно решить, добавив сначала текстовое поле в scrollview того же размера, а затем добавив его в основное представление прокрутки. вместо прямого добавления в основной scrollview

    // 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

Ответ 6

Так я это делаю:

Это очень просто, вы можете вернуть свой собственный контент для любого scrollRectToVisible.

Таким образом, вы не наносите вред нормальному поведению и потоку вещей - просто предоставляете одни и те же функции в одном канале с собственными улучшениями.

#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

Ответ 7

Я пробовал ответить @TaketoSano, но, похоже, не работает. Мое дело в том, что у меня нет scrollview, просто просмотр с несколькими текстовыми полями.

И наконец, у меня есть обходное решение. Для клавиатуры требуются два имени по умолчанию:

  • 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., изменение кадра представления будет автоматически применено к анимации, это действительно идеально!

Ответ 8

Основываясь на ответе Люка, чтобы решить проблему, что его решение полностью отключает автопрокрутку, вы можете отключить его выборочно следующим образом:

//  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, чтобы отключить автопрокрутку, но в любой момент можно полностью включить его, чтобы снова включить его (хотя почему вы хотите, чтобы это было выше меня).

Ответ 9

Я не знаю какого-либо свойства UIScrollView, которое позволит это. Было бы плохой пользовательский интерфейс, чтобы иметь возможность отключить это, ИМХО.

Тем не менее, возможно подкласс UIScrollView и переопределить некоторые из его методов, чтобы проверить, что поле UIText не является первым ответчиком перед прокруткой.

Ответ 10

У меня есть представление коллекции с текстовым полем на самом верху, подражая 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];
    }
}

Ответ 11

Более простой способ остановить прокрутку прокрутки, когда вы выбираете textField, находится в вашем viewController:: viewWillAppear() НЕ вызывать [super viewWillAppear];

Затем вы можете управлять прокруткой по своему усмотрению.