66

UITextField嵌入在 a 中的UIScrollView成为第一响应者(例如,通过用户输入某个字符)时,会UIScrollView自动滚动到该字段。有没有办法禁用它?

重复 rdar://16538222

4

11 回答 11

64

基于Moshe 的回答......子类UIScrollView化并覆盖以下方法:

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

留空。任务完成!


在斯威夫特:

class CustomScrollView: UIScrollView {
    override func scrollRectToVisible(_ rect: CGRect, animated: Bool) { }
}
于 2012-09-28T13:19:27.987 回答
59

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!

于 2011-04-15T06:13:23.737 回答
9

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用于恢复默认滚动行为。

于 2015-08-31T13:57:10.710 回答
8

正如 Taketo 所提到的,当 aUITextField成为第一响应者时,它的第一个类型的父视图UIScrollView(如果存在)会滚动以使其UITextField可见。最简单的技巧是将每个 UITextField 简单地包装在 a 中UIScrollView(或者理想情况下,将它们全部包装在一个 dummy 中UIScrollView)。这与 Taketo 的解决方案非常相似,但它应该会给您带来更好的性能,并且在我看来它将使您的代码(或 Interface Builder 中的界面)更加干净。

于 2012-01-20T00:27:16.550 回答
3

基于 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 中完全设置它以禁用自动滚动,但随时可以完全控制以重新启用它(尽管您想要这样做的原因超出了我的范围)。

于 2017-10-11T13:52:24.780 回答
2

它看起来像包含 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
于 2014-11-19T08:55:18.907 回答
1

这就是我这样做的方式:

这很简单,你可以为任何 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
于 2013-01-23T08:24:32.940 回答
0

我有一个集合视图,顶部有一个文本字段,模仿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];
    }
}
于 2013-12-09T19:27:16.187 回答
0

我不知道有任何财产UIScrollView会允许这样做。恕我直言,能够禁用它将是糟糕的用户体验。

也就是说,可以继承UIScrollView并覆盖它的一些方法,以在滚动之前检查它UITextfield是否不是第一响应者。

于 2011-01-03T16:25:42.010 回答
0

我已经尝试过@TaketoSano 的答案,但似乎不起作用。我的情况是我没有滚动视图,只有一个包含多个文本字段的视图。

最后,我找到了解决方法。我需要两个默认的键盘通知名称:

  • 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}}是键盘显示时的视图框架。

顺便说一句,视图的框架变化会自动应用动画,这真的很完美!

于 2013-05-26T14:04:45.673 回答
-6

选择 textField 时停止滚动视图滚动的更简单方法是在 viewController::viewWillAppear() 中不要调用 [super viewWillAppear];

然后,您可以根据需要控制滚动。

于 2014-02-05T17:21:29.820 回答