当 UITextField 成为第一响应者时禁用 UIScrollView 滚动

发布于 2024-10-10 07:03:18 字数 235 浏览 10 评论 0原文

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

重复的 rdar://16538222

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(11

栀子花开つ 2024-10-17 07:03:18

建立在 Moshe 的答案...子类 UIScrollView 并重写以下方法:

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

将其留空。工作完成了!


在斯威夫特中:

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

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) { }
}
鸠书 2024-10-17 07:03:18

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

我研究了如何通过跟踪调用跟踪来完成自动滚动,并发现当在 UITextField[UIFieldEditor rollSelectionToVisible] >。此方法似乎作用于 UITextField 最近祖先的 UIScrollView

因此,在 textFieldDidBeginEditing 上,通过使用大小相同的新 UIScrollView 包装 UITextField(即将视图插入到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];  
}  

希望这有帮助!

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!

才能让你更想念 2024-10-17 07:03:18

我在禁用作为 UITableView 单元格的 UITextView 的自动滚动时遇到了同样的问题。我能够使用以下方法解决它:

@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 用于在用户自己启动滚动时恢复默认滚动行为。

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.

我早已燃尽 2024-10-17 07:03:18

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

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.

暗地喜欢 2024-10-17 07:03:18

根据 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 中完全设置它以禁用自动滚动,但可以完全控制任何时候重新启用它(尽管我不知道你为什么想要这样做)。

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).

云归处 2024-10-17 07:03:18

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

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
末蓝 2024-10-17 07:03:18

我就是这样做的:

非常简单,您可以为任何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

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
<逆流佳人身旁 2024-10-17 07:03:18

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

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

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!

叫嚣ゝ 2024-10-17 07:03:18

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

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];
    }
}
深海夜未眠 2024-10-17 07:03:18

我不知道 UIScrollView 的任何属性允许这样做。恕我直言,如果能够禁用它,用户体验会很差。

也就是说,可以子类化 UIScrollView 并重写它的一些方法,以在滚动之前检查 UITextfield 不是第一响应者。

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.

莫言歌 2024-10-17 07:03:18

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

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

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文