【问题标题】:Disable UIScrollView scrolling when UITextField becomes first responder当 UITextField 成为第一响应者时禁用 UIScrollView 滚动
【发布时间】:2011-06-02 21:52:50
【问题描述】:

当嵌入在UIScrollView 中的UITextField 成为第一响应者时(例如,通过用户输入某些字符),UIScrollView 会自动滚动到该字段。有什么办法可以禁用吗?

Duplicate rdar://16538222

【问题讨论】:

  • 您使用的是UIViewController 还是UITableViewController?对于后者,这是标准且通常需要的行为。
  • 好吧,我正在使用嵌入在 UIScrollView 中的 UITextField。我正在模拟 SMS 应用程序“To”字段,所以每当用户输入一个字母时,我都会滚动到正在编辑的行(通过设置 UIScrollView 的 contentOffset)——如果你知道的话,就像在 Three20 中一样。发生的事情是,当用户键入一个字母时,UIScrollView 滚动到编辑行,使 UITextField 的原点超出了 scrollView 的 contentSize,然后当用户键入另一个字母时,它向上滚动到 UITextField 的原点(SCrollView 的默认行为),所以我得到这个上下行为
  • 我同意,应该有办法手动禁用它,但我也找不到。由于这种行为,我发现自己与滚动系统作斗争。

标签: ios swift uiscrollview uitextfield


【解决方案1】:

基于Moshe's answer... 子类UIScrollView 并覆盖以下方法:

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

留空。大功告成!


在斯威夫特中:

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

【讨论】:

  • 这里提到了这个解决方案:iphonedevsdk.com/forum/iphone-sdk-development/…
  • 简单优雅的解决方案。干杯!!
  • 这意味着如果您希望手动使用 ScrollRectToVisible 方法进行任何操作,它将无法使用。如果您不想这样做,这是一个很好的解决方案。
  • 使用此解决方法后,我开始在 Firebase 上收到“CALayer bounds contains NaN”错误报告。
【解决方案2】:

我一直在为同样的问题苦苦挣扎,终于找到了解决方案。

我通过跟踪调用跟踪调查了自动滚动是如何完成的,发现当在UITextField 中输入字母时会调用内部[UIFieldEditor scrollSelectionToVisible]。此方法似乎作用于UITextField 的最近祖先的UIScrollView

因此,在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];  
}  

希望这会有所帮助!

【讨论】:

  • 请注意:textFieldDidBeginEditing 应该是 textFieldShouldBeginEditing 否则它工作正常
  • +1 不是最干净的解决方案,但肯定会引导正确的方向:)
  • 天哪。武藤,非常感谢你!和 Rabih,非常感谢您的提问!我永远也想不通。如果可以的话,我会给这 100 个赞。
  • 赢了!这一直困扰着我整个星期(并且,在这个项目的早期版本中,一年多了)。谢谢。正如在其他 cmets 中所指出的,我发现将文本字段 always 包裹在相同大小的 UIScrollView 中更简单——不需要交换视图。
  • 这对使用 iOS 5.1 的任何人都有效吗?我似乎无法使窍门起作用!
【解决方案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】:

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

【讨论】:

  • 使用TPKeyboardAvoidingScrollView 对我有用。谢谢!
  • 很好的解决方法!我们可以使用一个 contentView 来保存 textFields,这个 contentView 应该是 scrollView 但不能滚动。
【解决方案5】:

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

【讨论】:

    【解决方案6】:

    看起来像包含 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
    

    【讨论】:

      【解决方案7】:

      这就是我的做法:

      这很简单,你可以为任何 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
      

      【讨论】:

        【解决方案8】:

        我尝试过@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}} 是键盘显示时的视图框架。

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

        【讨论】:

          【解决方案9】:

          我有一个集合视图,顶部有一个文本字段,模仿 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];
              }
          }
          

          【讨论】:

            【解决方案10】:

            我不知道UIScrollView 的任何属性会允许这样做。恕我直言,能够禁用该功能将是糟糕的用户体验。

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

            【讨论】:

            • 它没有用,您能否详细查看我对 Ortwin 的回答的评论?
            【解决方案11】:

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

            然后您可以随意控制滚动。

            【讨论】:

            • 您将在此处不调用超类实现,从而为您的应用引入很多 奇怪的行为。我建议不要这样做。
            猜你喜欢
            • 2012-10-14
            • 2018-02-17
            • 2011-06-25
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多