Disable UIScrollView scrolling when UITextField becomes first responder

IosSwiftUiscrollviewUitextfield

Ios Problem Overview


When a UITextField embedded in a UIScrollView becomes the first responder (for example, by the user typing in some character), the UIScrollView scrolls to that Field automatically. Is there any way to disable that?

Duplicate rdar://16538222

Ios Solutions


Solution 1 - Ios

Building on Moshe's answer... Subclass UIScrollView and override the following method:

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

Leave it empty. Job done!


In Swift:

class CustomScrollView: UIScrollView {
    override func scrollRectToVisible(_ rect: CGRect, animated: Bool) { }
}

Solution 2 - Ios

I've been struggling with the same problem, and at last I've found a solution.

I've investigated how the auto-scroll is done by tracking the call-trace, and found that an internal [UIFieldEditor scrollSelectionToVisible] is called when a letter is typed into the UITextField. This method seems to act on the UIScrollView of the nearest ancestor of the UITextField.

So, on textFieldDidBeginEditing, by wrapping the UITextField with a new UIScrollView with the same size of it (that is, inserting the view in between the UITextField and it's superview), this will block the auto-scroll. Finally remove this wrapper on textFieldDidEndEditing.

The code goes like:

- (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];  
}  

hope this helps!

Solution 3 - Ios

I had the same issue with disabling auto-scrolling of a UITextView being a cell of UITableView. I was able to resolve it using the following approach:

@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;
}

Defining scrollViewWillBeginDragging is used for restoring the default scrolling behaviour, when the user himself initiates scrolling.

Solution 4 - Ios

As Taketo mentioned, when a UITextField is made first responder, its first parent view that is of type UIScrollView (if one exists) is scrolled to make that UITextField visible. The easiest hack is to simply wrap each UITextField in a UIScrollView (or ideally, wrap all of them in a single dummy UIScrollView). This is very similar to Taketo's solution, but it should give you slightly better performance, and it will keep your code (or your interface in Interface Builder) much cleaner in my opinion.

Solution 5 - Ios

Building on Luke's answer, to handle the issue that his solution completely disables auto-scroll, you can disable it selectively as follows:

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

This way, you can completely set it up in Interface Builder to disable the auto-scroll, but have full control at any time to re-enable it (though why you'd want to is beyond me).

Solution 6 - Ios

It looks like UIScrollview which contains UITextfield, auto adjusts its content offset; when textfield is going to become first responder. This can be solved by adding textfield in scrollview of same size first, and then adding in to main scroll view. instead of directly adding in to main 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

Solution 7 - Ios

This is the way I do it:

It is very simple, you get to return your own contentOffset for any scrollRectToVisible.

This way you are not harming the normal behaviour and flow of things - just providing the same functionality in the same channel, with your own improvements.

#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

Solution 8 - Ios

I don't know of any property of UIScrollView that would allow that. It would be poor user experience to be able to disable that, IMHO.

That said, it may be possible to subclass UIScrollView and override some of its methods to check that the UITextfield is not a first responder before scrolling.

Solution 9 - Ios

I've tried @TaketoSano's answer, but seems not works.. My case is that I don't have a scrollview, just a view with several text fields.

And finally, I got a workaround. There're two default notification names for keyboard that I need:

  • UIKeyboardDidShowNotification when the keyboard did show;
  • UIKeyboardWillHideNotification when the keyboard will hide.

Here's the sample code I used:

- (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}}];
}

Here, the (CGRect){{272.f, 226.5f}, {480.f, 315.f}} is view's default frame when keyboard is hidden. And (CGRect){{272.f, 55.f}, {480.f, 315.f}} is view's frame when keyboard did show.

And b.t.w., the view's frame changing will be applied animation automatically, this's really perfect!

Solution 10 - Ios

I have a collection view with a text field at the very top, mimicking the UITableView.tableHeaderView. This text field is located in the negative content offset space so that it doesn't interfere with the rest of the collection view. I basically am detecting whether or not the user is performing the scrolling in the scroll view and whether or not the text field is first responder and if the scroll view is being scrolled beyond the top of the scroll view's content inset. This exact code won't necessarily help anyone but they could manipulate it to fit their case.

- (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];
    }
}

Solution 11 - Ios

An easier way to stop the scrollview scrolling when you select a textField is in your viewController::viewWillAppear() DO NOT call [super viewWillAppear];

You can then control the scroll as you wish.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionRabihView Question on Stackoverflow
Solution 1 - IosLukeView Answer on Stackoverflow
Solution 2 - IosTaketo SanoView Answer on Stackoverflow
Solution 3 - IostonsoView Answer on Stackoverflow
Solution 4 - IosdanbretlView Answer on Stackoverflow
Solution 5 - Iosmbm29414View Answer on Stackoverflow
Solution 6 - IosAmit TandelView Answer on Stackoverflow
Solution 7 - Iosdaniel.gindiView Answer on Stackoverflow
Solution 8 - IosMosheView Answer on Stackoverflow
Solution 9 - IosKjulyView Answer on Stackoverflow
Solution 10 - IosTPoschelView Answer on Stackoverflow
Solution 11 - IosnoRemaView Answer on Stackoverflow