我UITextFields
上有6 UIScrollView
。现在,我可以按用户请求滚动。但是当键盘出现时,会隐藏一些文本字段。
这不是用户友好的。
如何以编程方式滚动视图以确保键盘不会隐藏文本字段?
答案 0 :(得分:77)
这对我有用。在为键盘调整视图之前,有一个实例变量保存UIScrollView的偏移量的值,这样您就可以在UITextField返回后恢复之前的状态:
//header
@interface TheViewController : UIViewController <UITextFieldDelegate> {
CGPoint svos;
}
//implementation
- (void)textFieldDidBeginEditing:(UITextField *)textField {
svos = scrollView.contentOffset;
CGPoint pt;
CGRect rc = [textField bounds];
rc = [textField convertRect:rc toView:scrollView];
pt = rc.origin;
pt.x = 0;
pt.y -= 60;
[scrollView setContentOffset:pt animated:YES];
}
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
[scrollView setContentOffset:svos animated:YES];
[textField resignFirstResponder];
return YES;
}
答案 1 :(得分:38)
最后,一个简单的解决方法:
UIScrollView* v = (UIScrollView*) self.view ;
CGRect rc = [textField bounds];
rc = [textField convertRect:rc toView:v];
rc.origin.x = 0 ;
rc.origin.y -= 60 ;
rc.size.height = 400;
[self.scroll scrollRectToVisible:rc animated:YES];
现在我认为只是将它与上面的链接结合起来并设置好了!
答案 2 :(得分:22)
我已经整理了一个通用的,插入式UIScrollView和UITableView子类,它负责将其中的所有文本字段移出键盘。
当键盘即将出现时,子类将找到即将被编辑的子视图,并调整其帧和内容偏移量以确保视图可见,并使用与键盘弹出窗口匹配的动画。当键盘消失时,它会恢复其先前的大小。
它应该基本上适用于任何设置,可以是基于UITableView的界面,也可以是手动放置的视图。
Here它是。
(For google: TPKeyboardAvoiding, TPKeyboardAvoidingScrollView, TPKeyboardAvoidingCollectionView.)
Editor's note: TPKeyboardAvoiding seems to be continually updated and fresh, as of 2014.
答案 3 :(得分:2)
如果将文本字段的delegate
设置为程序中的控制器对象,则可以使该对象实现textFieldDidBeginEditing:
和textFieldShouldReturn:
方法。然后可以使用第一种方法滚动到您的文本字段,第二种方法可以用于向后滚动。
您可以在我的博客中找到我用过的代码:Sliding UITextViews around to avoid the keyboard。我没有在UIScrollView
中测试此代码的文本视图,但它应该可以正常工作。
答案 4 :(得分:2)
简单而且最好
- (void)textFieldDidBeginEditing:(UITextField *)textField
{
// self.scrlViewUI.contentOffset = CGPointMake(0, textField.frame.origin.y);
[_scrlViewUI setContentOffset:CGPointMake(0,textField.center.y-90) animated:YES];
tes=YES;
[self viewDidLayoutSubviews];
}
答案 5 :(得分:1)
到目前为止发布的答案对我不起作用,因为我有一个非常深的嵌套结构的UIViews。此外,我遇到的问题是,其中一些答案仅适用于某些设备方向。
这是我的解决方案,希望能让你浪费更少的时间。
我的UIViewTextView派生自UIView,是一个UITextView委托,在从UITextView的XML文件中读取了一些参数之后添加了一个UITextView(为了清楚起见,这里省略了XML部分)。
这是私有接口定义:
#import "UIViewTextView.h"
#import <CoreGraphics/CoreGraphics.h>
#import <CoreGraphics/CGColor.h>
@interface UIViewTextView (/**/) {
@private
UITextView *tf;
/*
* Current content scroll view
* position and frame
*/
CGFloat currentScrollViewPosition;
CGFloat currentScrollViewHeight;
CGFloat kbHeight;
CGFloat kbTop;
/*
* contentScrollView is the UIScrollView
* that contains ourselves.
*/
UIScrollView contentScrollView;
}
@end
在init方法中,我必须注册事件处理程序:
@implementation UIViewTextView
- (id) initWithScrollView:(UIScrollView*)scrollView {
self = [super init];
if (self) {
contentScrollView = scrollView;
// ...
tf = [[UITextView alloc] initWithFrame:CGRectMake(0, 0, 241, 31)];
// ... configure tf and fetch data for it ...
tf.delegate = self;
// ...
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
[nc addObserver:self selector:@selector(keyboardWasShown:) name: UIKeyboardWillShowNotification object:nil];
[nc addObserver:self selector:@selector(keyboardWasHidden:) name: UIKeyboardWillHideNotification object:nil];
[self addSubview:tf];
}
return(self);
}
一旦完成,我们需要处理键盘节目事件。这在调用textViewBeginEditing之前被调用,因此我们可以使用它来找出键盘的一些属性。实质上,我们想知道键盘的高度。不幸的是,这需要从横向模式的宽度属性中获取:
-(void)keyboardWasShown:(NSNotification*)aNotification {
NSDictionary* info = [aNotification userInfo];
CGRect kbRect = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue];
CGSize kbSize = kbRect.size;
CGRect screenRect = [[UIScreen mainScreen] bounds];
CGFloat sWidth = screenRect.size.width;
CGFloat sHeight = screenRect.size.height;
UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];
if ((orientation == UIDeviceOrientationPortrait)
||(orientation == UIDeviceOrientationPortraitUpsideDown)) {
kbHeight = kbSize.height;
kbTop = sHeight - kbHeight;
} else {
//Note that the keyboard size is not oriented
//so use width property instead
kbHeight = kbSize.width;
kbTop = sWidth - kbHeight;
}
接下来,我们需要在开始编辑时实际滚动。我们在这里这样做:
- (void) textViewDidBeginEditing:(UITextView *)textView {
/*
* Memorize the current scroll position
*/
currentScrollViewPosition = contentScrollView.contentOffset.y;
/*
* Memorize the current scroll view height
*/
currentScrollViewHeight = contentScrollView.frame.size.height;
// My top position
CGFloat myTop = [self convertPoint:self.bounds.origin toView:[UIApplication sharedApplication].keyWindow.rootViewController.view].y;
// My height
CGFloat myHeight = self.frame.size.height;
// My bottom
CGFloat myBottom = myTop + myHeight;
// Eventual overlap
CGFloat overlap = myBottom - kbTop;
/*
* If there's no overlap, there's nothing to do.
*/
if (overlap < 0) {
return;
}
/*
* Calculate the new height
*/
CGRect crect = contentScrollView.frame;
CGRect nrect = CGRectMake(crect.origin.x, crect.origin.y, crect.size.width, currentScrollViewHeight + overlap);
/*
* Set the new height
*/
[contentScrollView setFrame:nrect];
/*
* Set the new scroll position
*/
CGPoint npos;
npos.x = contentScrollView.contentOffset.x;
npos.y = contentScrollView.contentOffset.y + overlap;
[contentScrollView setContentOffset:npos animated:NO];
}
当我们结束编辑时,我们这样做是为了重置滚动位置:
- (void) textViewDidEndEditing:(UITextView *)textView {
/*
* Reset the scroll view position
*/
CGRect crect = contentScrollView.frame;
CGRect nrect = CGRectMake(crect.origin.x, crect.origin.y, crect.size.width, currentScrollViewHeight);
[contentScrollView setFrame:nrect];
/*
* Reset the scroll view height
*/
CGPoint npos;
npos.x = contentScrollView.contentOffset.x;
npos.y = currentScrollViewPosition;
[contentScrollView setContentOffset:npos animated:YES];
[tf resignFirstResponder];
// ... do something with your data ...
}
键盘上没有什么可以做的隐藏事件处理程序;无论如何我们都留下了:
-(void)keyboardWasHidden:(NSNotification*)aNotification {
}
就是这样。
/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect
{
// Drawing code
}
*/
@end
答案 6 :(得分:1)
我知道这已经过时了,但上面的解决方案仍然没有完美的“完美”无错误,向后兼容和无闪烁动画所需的所有花哨的定位功能。
让我分享我的解决方案(假设您已设置UIKeyboardWill(Show|Hide)Notification
):
// Called when UIKeyboardWillShowNotification is sent
- (void)keyboardWillShow:(NSNotification*)notification
{
// if we have no view or are not visible in any window, we don't care
if (!self.isViewLoaded || !self.view.window) {
return;
}
NSDictionary *userInfo = [notification userInfo];
CGRect keyboardFrameInWindow;
[[userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] getValue:&keyboardFrameInWindow];
// the keyboard frame is specified in window-level coordinates. this calculates the frame as if it were a subview of our view, making it a sibling of the scroll view
CGRect keyboardFrameInView = [self.view convertRect:keyboardFrameInWindow fromView:nil];
CGRect scrollViewKeyboardIntersection = CGRectIntersection(_scrollView.frame, keyboardFrameInView);
UIEdgeInsets newContentInsets = UIEdgeInsetsMake(0, 0, scrollViewKeyboardIntersection.size.height, 0);
// this is an old animation method, but the only one that retains compaitiblity between parameters (duration, curve) and the values contained in the userInfo-Dictionary.
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:[[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue]];
[UIView setAnimationCurve:[[userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] intValue]];
_scrollView.contentInset = newContentInsets;
_scrollView.scrollIndicatorInsets = newContentInsets;
/*
* Depending on visual layout, _focusedControl should either be the input field (UITextField,..) or another element
* that should be visible, e.g. a purchase button below an amount text field
* it makes sense to set _focusedControl in delegates like -textFieldShouldBeginEditing: if you have multiple input fields
*/
if (_focusedControl) {
CGRect controlFrameInScrollView = [_scrollView convertRect:_focusedControl.bounds fromView:_focusedControl]; // if the control is a deep in the hierarchy below the scroll view, this will calculate the frame as if it were a direct subview
controlFrameInScrollView = CGRectInset(controlFrameInScrollView, 0, -10); // replace 10 with any nice visual offset between control and keyboard or control and top of the scroll view.
CGFloat controlVisualOffsetToTopOfScrollview = controlFrameInScrollView.origin.y - _scrollView.contentOffset.y;
CGFloat controlVisualBottom = controlVisualOffsetToTopOfScrollview + controlFrameInScrollView.size.height;
// this is the visible part of the scroll view that is not hidden by the keyboard
CGFloat scrollViewVisibleHeight = _scrollView.frame.size.height - scrollViewKeyboardIntersection.size.height;
if (controlVisualBottom > scrollViewVisibleHeight) { // check if the keyboard will hide the control in question
// scroll up until the control is in place
CGPoint newContentOffset = _scrollView.contentOffset;
newContentOffset.y += (controlVisualBottom - scrollViewVisibleHeight);
// make sure we don't set an impossible offset caused by the "nice visual offset"
// if a control is at the bottom of the scroll view, it will end up just above the keyboard to eliminate scrolling inconsistencies
newContentOffset.y = MIN(newContentOffset.y, _scrollView.contentSize.height - scrollViewVisibleHeight);
[_scrollView setContentOffset:newContentOffset animated:NO]; // animated:NO because we have created our own animation context around this code
} else if (controlFrameInScrollView.origin.y < _scrollView.contentOffset.y) {
// if the control is not fully visible, make it so (useful if the user taps on a partially visible input field
CGPoint newContentOffset = _scrollView.contentOffset;
newContentOffset.y = controlFrameInScrollView.origin.y;
[_scrollView setContentOffset:newContentOffset animated:NO]; // animated:NO because we have created our own animation context around this code
}
}
[UIView commitAnimations];
}
// Called when the UIKeyboardWillHideNotification is sent
- (void)keyboardWillHide:(NSNotification*)notification
{
// if we have no view or are not visible in any window, we don't care
if (!self.isViewLoaded || !self.view.window) {
return;
}
NSDictionary *userInfo = notification.userInfo;
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:[[userInfo valueForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue]];
[UIView setAnimationCurve:[[userInfo valueForKey:UIKeyboardAnimationCurveUserInfoKey] intValue]];
// undo all that keyboardWillShow-magic
// the scroll view will adjust its contentOffset apropriately
_scrollView.contentInset = UIEdgeInsetsZero;
_scrollView.scrollIndicatorInsets = UIEdgeInsetsZero;
[UIView commitAnimations];
}
答案 7 :(得分:1)
您可以查看它:https://github.com/michaeltyson/TPKeyboardAvoiding(我将该示例用于我的应用)。它工作得很好。我希望能帮助你。
实际上,这是一个关于使用TPKeyboardAvoiding的完整教程,可以帮助某人
(1)从github链接下载 zip文件。将这些四个文件添加到您的Xcode项目中:
(2)在IB中建立你的美丽形式。添加UIScrollView。 坐在滚动视图中的表单项。 (注意 - 关于界面构建器的非常有用的提示:https://stackoverflow.com/a/16952902/294884)
(3)点击滚动视图上的。然后在右上角的第三个按钮,你会看到单词“UIScrollView”。使用复制和粘贴,将其更改为“TPKeyboardAvoidingScrollView”
(4)就是这样。将应用程序放入应用商店,并向您的客户收费。
(另外,只需点击滚动视图的“检查器”标签。您可能更喜欢打开或关闭弹跳和滚动条 - 您的偏好。)
个人评论 - 我强烈建议在几乎所有情况下都使用滚动视图(或集合视图)作为输入表单。 不要使用表格视图。出于多种原因存在问题。而且非常简单,使用滚动视图非常容易。只要你想要的任何方式。它在界面构建器中是100%所见即所得。希望它有所帮助
答案 8 :(得分:1)
这是我的代码,希望它能帮到你。如果您有许多文本字段
,它可以正常工作CGPoint contentOffset;
bool isScroll;
- (void)textFieldDidBeginEditing:(UITextField *)textField {
contentOffset = self.myScroll.contentOffset;
CGPoint newOffset;
newOffset.x = contentOffset.x;
newOffset.y = contentOffset.y;
//check push return in keyboar
if(!isScroll){
//180 is height of keyboar
newOffset.y += 180;
isScroll=YES;
}
[self.myScroll setContentOffset:newOffset animated:YES];
}
- (BOOL)textFieldShouldReturn:(UITextField *)textField{
//reset offset of content
isScroll = NO;
[self.myScroll setContentOffset:contentOffset animated:YES];
[textField endEditing:true];
return true;
}
我们有一个点contentOffset来在keyboar show之前保存scrollview的contentoffset。然后我们将滚动内容y约180(keyboar的高度)。当你在keyboar中触摸return时,我们会将内容滚动到旧点(它是contentOffset)。如果你有很多文本字段,你不要触摸键盘中的返回但是你触摸另一个文本字段,它将+180。所以我们检查触摸返回
答案 9 :(得分:0)
我修改了上述一些解决方案,以便更容易理解和使用。我使用了IBOutlet,以便多个文本字段可以通过文本字段的“已发送事件”中的“编辑已开始”链接到该功能。 **不要忘记为滚动视图添加插座
- (IBAction)moveViewUpwards:(id)sender
{
CGRect rc = [sender convertRect:[sender bounds] toView:scrollView];
rc.origin.x = 0 ;
rc.origin.y -= 60 ;
rc.size.height = 400;
[scrollView scrollRectToVisible:rc animated:YES];
}
答案 10 :(得分:0)
使用其中任何一个,
CGPoint bottomOffset = CGPointMake(0, self.MainScrollView.contentSize.height - self.MainScrollView.bounds.size.height);
[self.MainScrollView setContentOffset:bottomOffset animated:YES];
或
[self.MainScrollView scrollRectToVisible:CGRectMake(0, self.MainScrollView.contentSize.height - self.MainScrollView.bounds.size.height-30, MainScrollView.frame.size.width, MainScrollView.frame.size.height) animated:YES];
答案 11 :(得分:0)
我认为最好使用键盘通知,因为您不知道第一个响应者(重点关注的控件)是textField还是textView(或其他)。所以juste创建了一个类别来找到第一个响应者:
#import "UIResponder+FirstResponder.h"
static __weak id currentFirstResponder;
@implementation UIResponder (FirstResponder)
+(id)currentFirstResponder {
currentFirstResponder = nil;
[[UIApplication sharedApplication] sendAction:@selector(findFirstResponder:) to:nil from:nil forEvent:nil];
return currentFirstResponder;
}
-(void)findFirstResponder:(id)sender {
currentFirstResponder = self;
}
@end
然后
-(void)keyboardWillShowNotification:(NSNotification*)aNotification{
contentScrollView.delegate=nil;
contentScrollView.scrollEnabled=NO;
contentScrollViewOriginalOffset = contentScrollView.contentOffset;
UIResponder *lc_firstResponder = [UIResponder currentFirstResponder];
if([lc_firstResponder isKindOfClass:[UIView class]]){
UIView *lc_view = (UIView *)lc_firstResponder;
CGRect lc_frame = [lc_view convertRect:lc_view.bounds toView:contentScrollView];
CGPoint lc_point = CGPointMake(0, lc_frame.origin.y-lc_frame.size.height);
[contentScrollView setContentOffset:lc_point animated:YES];
}
}
最终禁用滚动并将委托设置为nil然后将其恢复以避免在第一个响应者的编辑期间执行某些操作。就像james_womack所说的那样,保持原始偏移量以在keyboardWillHideNotification方法中恢复它。
-(void)keyboardWillHideNotification:(NSNotification*)aNotification{
contentScrollView.delegate=self;
contentScrollView.scrollEnabled=YES;
[contentScrollView setContentOffset:contentScrollViewOriginalOffset animated:YES];
}
答案 12 :(得分:0)
在Swift 1.2+中做这样的事情:
class YourViewController: UIViewController, UITextFieldDelegate {
override func viewDidLoad() {
super.viewDidLoad()
_yourTextField.delegate = self //make sure you have the delegate set to this view controller for each of your textFields so textFieldDidBeginEditing can be called for each one
...
}
func textFieldDidBeginEditing(textField: UITextField) {
var point = textField.convertPoint(textField.frame.origin, toView: _yourScrollView)
point.x = 0.0 //if your textField does not have an origin at 0 for x and you don't want your scrollView to shift left and right but rather just up and down
_yourScrollView.setContentOffset(point, animated: true)
}
func textFieldDidEndEditing(textField: UITextField) {
//Reset scrollview once done editing
scrollView.setContentOffset(CGPoint.zero, animated: true)
}
}