【发布时间】:2011-11-02 10:31:28
【问题描述】:
UITextView 插入到UITabBarController 的标签中(在 iPhone 上)。
- 用很多行填充
UITextView。 - 显示键盘以编辑文本。
发生了什么?键盘用光标隐藏了UITextView 的一半。结果无法编辑文本。
如何解决所有 Apple 移动设备(屏幕分辨率不同)的问题?非常感谢您的帮助!
【问题讨论】:
标签: iphone keyboard uitextview
UITextView 插入到UITabBarController 的标签中(在 iPhone 上)。
UITextView。发生了什么?键盘用光标隐藏了UITextView 的一半。结果无法编辑文本。
如何解决所有 Apple 移动设备(屏幕分辨率不同)的问题?非常感谢您的帮助!
【问题讨论】:
标签: iphone keyboard uitextview
以下代码达到了最好的结果。另外不要忘记将背景颜色设置为 UIView 并将 UITextView 放在其他顶部屏幕控件(例如 UITabBar)之前。
文本的最终编辑现在仍然不完美。你可以尝试改进。
FirstViewController.h:
@interface FirstViewController : UIViewController {
IBOutlet UIBarButtonItem *buttonDone;
IBOutlet UITextView *textView;
UITabBarController* tabBarController; // set from superview in AppDelegate (MainWindow.xib)
}
@property (nonatomic, retain) UITabBarController* tabBarController;
FirstViewController.m:
@synthesize tabBarController;
- (void)viewDidAppear:(BOOL)animated
{
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShown:) name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
}
- (void)viewWillDisappear:(BOOL)animated
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (void)moveTextViewForKeyboard:(NSNotification*)aNotification up:(BOOL)up {
NSDictionary* userInfo = [aNotification userInfo];
NSTimeInterval animationDuration;
UIViewAnimationCurve animationCurve;
CGRect keyboardEndFrame;
[[userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] getValue:&animationCurve];
[[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] getValue:&animationDuration];
[[userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] getValue:&keyboardEndFrame];
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:animationDuration];
[UIView setAnimationCurve:animationCurve];
CGRect newFrame = textView.frame;
CGRect keyboardFrame = [self.view convertRect:keyboardEndFrame toView:nil];
keyboardFrame.size.height -= tabBarController.tabBar.frame.size.height;
newFrame.size.height -= keyboardFrame.size.height * (up?1:-1);
textView.frame = newFrame;
[UIView commitAnimations];
}
- (void)keyboardWillShown:(NSNotification*)aNotification
{
buttonDone.enabled = true;
[self moveTextViewForKeyboard:aNotification up:YES];
}
- (void)keyboardWillHide:(NSNotification*)aNotification
{
buttonDone.enabled = false;
[self moveTextViewForKeyboard:aNotification up:NO];
}
附:没有 stackoverflow 很难为 iOS 编写代码......
【讨论】:
使用自动布局,处理起来要容易得多(前提是您了解自动布局):
您无需尝试识别受影响的视图并调整其大小,只需为您的所有视图内容创建一个父框架。然后,如果 kbd 出现,您调整框架的大小,并且如果您正确设置了约束,视图将很好地重新排列其所有子视图。无需为此摆弄大量难以阅读的代码。
事实上,在similar question 中,我找到了指向此excellent tutorial 的有关此技术的链接。
【讨论】:
我在尝试让我的文本视图在 iOS 7 和 iOS 8 以及新的 QuickType 功能下正确滚动和动画时遇到了几个问题。起初我专注于动画滚动视图插图,但 iOS 7 和 8 之间的行为不同,无法让它在两者上正常工作。
然后我意识到我可以通过只关注框架来简化事情,这对我来说可以使用更简单的代码。总结:
UIKeyboardDidChangeFrameNotification(这将在显示/隐藏 QuickType 时通知)。以下是一些说明上述内容的代码:
- (void)viewDidLoad {
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardDidChangeFrameWithNotification:) name:UIKeyboardDidChangeFrameNotification object:nil];
}
- (void)keyboardDidChangeFrameWithNotification:(NSNotification *)notification {
CGFloat keyboardVerticalIncrease = [self keyboardVerticalIncreaseForNotification:notification];
[self animateTextViewFrameForVerticalOffset:keyboardVerticalIncrease];
}
- (CGFloat)keyboardVerticalIncreaseForNotification:(NSNotification *)notification {
CGFloat keyboardBeginY = [notification.userInfo[UIKeyboardFrameBeginUserInfoKey] CGRectValue].origin.y;
CGFloat keyboardEndY = [notification.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue].origin.y;
CGFloat keyboardVerticalIncrease = keyboardBeginY - keyboardEndY;
return keyboardVerticalIncrease;
}
- (void)animateTextViewFrameForVerticalOffset:(CGFloat)offset {
CGFloat constant = self.bottomConstraint.constant;
CGFloat newConstant = constant + offset;
self.bottomConstraint.constant = newConstant;
[self.view layoutIfNeeded];
[UIView animateWithDuration:0.5 animations:^{
[self.view layoutIfNeeded];
}];
}
关于动画的简要说明。我使用了 Autolayout,所以我选择为文本视图的 NSAutoLayoutConstraint 设置动画,而不是直接为框架设置动画。为此,我在动画块内的 和 之前调用 [self.view layoutIfNeeded]。这是动画约束的正确方法。我找到了这个提示here。
【讨论】:
值得注意的是,仅当设备处于纵向模式(而不是倒置)时,赞成的答案才有效,在其他模式下,边界会出错。我相信你可以通过使用边界来修复这个问题,但我无法让它工作,所以下面的调整对我有用:
- (void)moveTextViewForKeyboard:(NSNotification*)aNotification up:(BOOL)up {
NSDictionary* userInfo = [aNotification userInfo];
NSTimeInterval animationDuration;
UIViewAnimationCurve animationCurve;
CGRect keyboardEndFrame;
[[userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] getValue:&animationCurve];
[[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] getValue:&animationDuration];
[[userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] getValue:&keyboardEndFrame];
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:animationDuration];
[UIView setAnimationCurve:animationCurve];
CGRect newFrame = self.view.frame;
if (keyboardEndFrame.size.height >keyboardEndFrame.size.width)
{ //we must be in landscape
if (keyboardEndFrame.origin.x==0)
{ //upside down so need to flip origin
newFrame.origin = CGPointMake(keyboardEndFrame.size.width, 0);
}
newFrame.size.width -= keyboardEndFrame.size.width * (up?1:-1);
} else
{ //in portrait
if (keyboardEndFrame.origin.y==0)
{
//upside down so need to flip origin
newFrame.origin = CGPointMake(0, keyboardEndFrame.size.height);
}
newFrame.size.height -= keyboardEndFrame.size.height * (up?1:-1);
}
self.view.frame = newFrame;
[UIView commitAnimations];
}
【讨论】:
多年过去了,问题仍然存在。苹果绝对应该自己处理所有这些事情。但事实并非如此。这是基于官方 Apple 的 documentation 以及错误修复的新解决方案。它支持 iOS 8、iOS 9、inputAccessoryView 并为新版本的 iOS 和新设备做好准备。
/* Apple's solution to resize keyboard but with accessory view support */
- (void)keyboardDidShow:(NSNotification*)aNotification {
NSDictionary* info = [aNotification userInfo];
CGRect keyboardFrame = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
double keyboardHeight = [[UIScreen mainScreen] bounds].size.height - keyboardFrame.origin.y;
UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, keyboardHeight, 0.0);
editor.contentInset = contentInsets;
editor.scrollIndicatorInsets = contentInsets;
}
- (void)keyboardWillHide:(NSNotification*)aNotification {
UIEdgeInsets contentInsets = UIEdgeInsetsZero;
editor.contentInset = contentInsets;
editor.scrollIndicatorInsets = contentInsets;
// button to hide the keyboard
buttonDone.enabled = false;
}
/* Fix issues with size classes and accessory view */
- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator {
[super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
// fix incorrect size of the inputAccessoryView when size class changed
// willTransitionToTraitCollection and traitCollectionDidChange can't help us
if (editor && editor.inputAccessoryView && !editor.inputAccessoryView.hidden) {
[editor resignFirstResponder];
}
}
/* Hide accessory view if a hardware keyboard is present */
#define gThresholdForHardwareKeyboardToolbar 160.f // it's minimum height of the software keyboard on iPhone 4 in landscape mode
- (bool)isExternalKeyboard:(NSNotification*)aNotification {
NSDictionary* info = [aNotification userInfo];
CGRect keyboardFrame = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
double keyboardHeight = [[UIScreen mainScreen] bounds].size.height - keyboardFrame.origin.y;
return keyboardHeight < gThresholdForHardwareKeyboardToolbar;
}
- (void)keyboardWillShow:(NSNotification*)aNotification {
if ([self isExternalKeyboard:aNotification]) {
// hardware keyboard is present
if (editor && editor.inputAccessoryView) {
editor.inputAccessoryView.hidden = true;
}
} else {
// only on-screen keyboard
if (editor && editor.inputAccessoryView) {
editor.inputAccessoryView.hidden = false;
}
}
// button to hide the keyboard
buttonDone.enabled = true;
}
【讨论】:
- (void)registerKeyboardNotifications
{
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:@selector(keyboardWillShow:)
name:UIKeyboardDidShowNotification
object:nil];
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:@selector(keyboardWillHide:)
name:UIKeyboardWillHideNotification
object:nil];
}
- (void)unregisterKeyboardNotifications
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
-(void) keyboardWillHide:(NSNotification *)note
{
//adjust frame
}
-(void) keyboardWillShow:(NSNotification *)note
{
//adjust frame
}
在dealloc中也取消注册通知
- (void)unregisterKeyboardNotifications
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
【讨论】:
首先在NSNotificationCenterdefaultCenter 中添加几个键盘方法
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:)
name:UIKeyboardWillShowNotification object:self.view.window];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide:)
name:UIKeyboardWillHideNotification object:self.view.window];
然后你可以改变尺寸:
- (void)keyboardWillShow:(NSNotification *)notif
{
[thetextView setFrame:CGRectMake(20, 49, 280, 187)]; //Or where ever you want the view to go
}
- (void)keyboardWillHide:(NSNotification *)notif
{
[thetextView setFrame:CGRectMake(20, 49, 280, 324)]; //return it to its original position
}
【讨论】:
简而言之,注册键盘通知并在收到通知时进行调整大小的工作。
【讨论】:
作为后续,当键盘通知发生时更新框架的技术不适用于 iOS 7。有关替代解决方案,请参阅以下内容:
【讨论】:
我在这里尝试了最佳答案,但我发现其中有问题。如果您在同一页面上有另一个文本字段,请单击该文本字段,显示键盘。您会注意到文本视图缩小了。但是,如果您现在单击文本视图,您会注意到文本视图大小再次缩小,而它不应该缩小。
我对这个问题的解决方案是在视图控制器中维护一个表示键盘状态(显示/隐藏)的属性。如果键盘当前可见,则不应缩小文本视图。如果您为不同的文本输入使用不同尺寸的键盘,您还应该保持旧的键盘尺寸。
请注意,此解决方案也没有考虑不同的方向,这可能会影响您计算文本视图大小的方式。
@implementation MyViewController {
BOOL keyboardShown;
NSInteger keyboardHeight;
}
- (void)moveTextViewForKeyboard:(NSNotification*)aNotification up: (BOOL) up{
NSDictionary* userInfo = [aNotification userInfo];
NSTimeInterval animationDuration;
UIViewAnimationCurve animationCurve;
CGRect keyboardEndFrame;
[[userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] getValue:&animationCurve];
[[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] getValue:&animationDuration];
[[userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] getValue:&keyboardEndFrame];
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:animationDuration];
[UIView setAnimationCurve:animationCurve];
CGRect newFrame = self.textView.frame;
CGRect keyboardFrame = [self.view convertRect:keyboardEndFrame toView:nil];
NSInteger oldHeight = self->keyboardShown ? self->keyboardHeight : 0;
NSInteger newHeight = up ? keyboardFrame.size.height : 0;
NSInteger change = oldHeight - newHeight;
self->keyboardShown = up;
self->keyboardHeight = keyboardFrame.size.height;
newFrame.size.height += change;
self.textView.frame = newFrame;
[UIView commitAnimations];
}
【讨论】: