当 UITextField 成为第一响应者时禁用 UIScrollView 滚动
Posted
技术标签:
【中文标题】当 UITextField 成为第一响应者时禁用 UIScrollView 滚动【英文标题】:Disable UIScrollView scrolling when UITextField becomes first responder 【发布时间】: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 的默认行为),所以我得到这个上下行为
我同意,应该有办法手动禁用它,但我也找不到。由于这种行为,我发现自己与滚动系统作斗争。
【参考方案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];
然后您可以随意控制滚动。
【讨论】:
您将在此处不调用超类实现,从而为您的应用引入很多 奇怪的行为。我建议不要这样做。以上是关于当 UITextField 成为第一响应者时禁用 UIScrollView 滚动的主要内容,如果未能解决你的问题,请参考以下文章
UITextField 不能成为 UITableView 中的第一响应者
UITextField, becomeFirstResponder, resignFirstResponder
为啥 uitextfield 在成为第一响应者时从不返回 nil?