当UITextField
嵌入在 a 中的UIScrollView
成为第一响应者(例如,通过用户输入某个字符)时,会UIScrollView
自动滚动到该字段。有没有办法禁用它?
11 回答
基于Moshe 的回答......子类UIScrollView
化并覆盖以下方法:
- (void)scrollRectToVisible:(CGRect)rect animated:(BOOL)animated
留空。任务完成!
在斯威夫特:
class CustomScrollView: UIScrollView {
override func scrollRectToVisible(_ rect: CGRect, animated: Bool) { }
}
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!
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
用于恢复默认滚动行为。
正如 Taketo 所提到的,当 aUITextField
成为第一响应者时,它的第一个类型的父视图UIScrollView
(如果存在)会滚动以使其UITextField
可见。最简单的技巧是将每个 UITextField 简单地包装在 a 中UIScrollView
(或者理想情况下,将它们全部包装在一个 dummy 中UIScrollView
)。这与 Taketo 的解决方案非常相似,但它应该会给您带来更好的性能,并且在我看来它将使您的代码(或 Interface Builder 中的界面)更加干净。
基于 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 中完全设置它以禁用自动滚动,但随时可以完全控制以重新启用它(尽管您想要这样做的原因超出了我的范围)。
它看起来像包含 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
这就是我这样做的方式:
这很简单,你可以为任何 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
我有一个集合视图,顶部有一个文本字段,模仿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];
}
}
我不知道有任何财产UIScrollView
会允许这样做。恕我直言,能够禁用它将是糟糕的用户体验。
也就是说,可以继承UIScrollView
并覆盖它的一些方法,以在滚动之前检查它UITextfield
是否不是第一响应者。
我已经尝试过@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}}
是键盘显示时的视图框架。
顺便说一句,视图的框架变化会自动应用动画,这真的很完美!
选择 textField 时停止滚动视图滚动的更简单方法是在 viewController::viewWillAppear() 中不要调用 [super viewWillAppear];
然后,您可以根据需要控制滚动。