我有一个包含全屏的视图控制器UITextView
。当显示键盘时,我想调整文本视图的大小,使其不会隐藏在键盘下。
这是 iOS 的一种相当标准的方法,如以下问题所述:
但是,在 iOS 7 中,如果用户点击屏幕下半部分的文本视图,当文本视图调整大小时,光标仍会离开屏幕。如果用户点击回车,文本视图只会滚动以使光标进入视图。
我有一个包含全屏的视图控制器UITextView
。当显示键盘时,我想调整文本视图的大小,使其不会隐藏在键盘下。
这是 iOS 的一种相当标准的方法,如以下问题所述:
但是,在 iOS 7 中,如果用户点击屏幕下半部分的文本视图,当文本视图调整大小时,光标仍会离开屏幕。如果用户点击回车,文本视图只会滚动以使光标进入视图。
我阅读了谈论这个话题的文档。我把它翻译成 Swift,它对我来说非常好用。
这用于像 iMessage 这样的整页 UITextView。
我在 XCode 6.2 上使用 iOS 8.2 和 Swift,这是我的代码。只需setupKeyboardNotifications
从您viewDidLoad
或其他初始化方法中调用它。
func setupKeyboardNotifications() {
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWasShown:"), name: UIKeyboardDidShowNotification, object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillBeHidden:"), name: UIKeyboardWillHideNotification, object: nil)
}
func keyboardWasShown(aNotification:NSNotification) {
let info = aNotification.userInfo
let infoNSValue = info![UIKeyboardFrameBeginUserInfoKey] as NSValue
let kbSize = infoNSValue.CGRectValue().size
let contentInsets = UIEdgeInsetsMake(0.0, 0.0, kbSize.height, 0.0)
codeTextView.contentInset = contentInsets
codeTextView.scrollIndicatorInsets = contentInsets
}
func keyboardWillBeHidden(aNotification:NSNotification) {
let contentInsets = UIEdgeInsetsZero
codeTextView.contentInset = contentInsets
codeTextView.scrollIndicatorInsets = contentInsets
}
此外,如果您在旋转时遇到插入符号在正确位置的问题,请检查方向变化并滚动到正确的位置。
override func didRotateFromInterfaceOrientation(fromInterfaceOrientation: UIInterfaceOrientation) {
scrollToCaretInTextView(codeTextView, animated: true)
}
func scrollToCaretInTextView(textView:UITextView, animated:Bool) {
var rect = textView.caretRectForPosition(textView.selectedTextRange?.end)
rect.size.height += textView.textContainerInset.bottom
textView.scrollRectToVisible(rect, animated: animated)
}
斯威夫特 3:
func configureKeyboardNotifications() {
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWasShown(aNotification:)), name: NSNotification.Name.UIKeyboardDidShow, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillBeHidden(aNotification:)), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
}
func keyboardWasShown(aNotification:NSNotification) {
let info = aNotification.userInfo
let infoNSValue = info![UIKeyboardFrameBeginUserInfoKey] as! NSValue
let kbSize = infoNSValue.cgRectValue.size
let contentInsets = UIEdgeInsetsMake(0.0, 0.0, kbSize.height, 0.0)
textView.contentInset = contentInsets
textView.scrollIndicatorInsets = contentInsets
}
func keyboardWillBeHidden(aNotification:NSNotification) {
let contentInsets = UIEdgeInsets.zero
textView.contentInset = contentInsets
textView.scrollIndicatorInsets = contentInsets
}
斯威夫特 4 和 5:
func setupKeyboardNotifications() {
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(_ :)), name: UIResponder.keyboardWillShowNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide(_:)), name: UIResponder.keyboardWillHideNotification, object: nil)
}
@objc func keyboardWillShow(_ notification:NSNotification) {
let d = notification.userInfo!
var r = (d[UIResponder.keyboardFrameEndUserInfoKey] as! NSValue).cgRectValue
r = self.textView.convert(r, from:nil)
self.textView.contentInset.bottom = r.size.height
self.textView.verticalScrollIndicatorInsets.bottom = r.size.height
}
@objc func keyboardWillHide(_ notification:NSNotification) {
let contentInsets = UIEdgeInsets.zero
self.textView.contentInset = contentInsets
self.textView.verticalScrollIndicatorInsets = contentInsets
}
使用自动布局,处理起来要容易得多(前提是您了解自动布局):
无需尝试识别和调整受影响的视图,您只需为所有视图的内容创建一个父框架。然后,如果 kbd 出现,您调整框架的大小,并且如果您正确设置了约束,视图将很好地重新排列其所有子视图。无需为此摆弄许多难以阅读的代码。
事实上,在一个类似的问题中,我找到了这个关于这项技术的优秀教程的链接。
此外,此处使用 textViewDidBeginEditing 而不是 UIKeyboardWillShowNotification 的其他示例有一个大问题:
如果用户连接了外部蓝牙键盘,那么即使没有出现屏幕键盘,控件仍会被向上推。这不好。
所以,总结一下:
或者,查看 LeoNatan 的回复。这甚至可能是一个更清洁、更简单的解决方案(我自己还没有尝试过)。
不要调整文本视图的大小。相反,将contentInset
and scrollIndicatorInsets
bottom 设置为键盘高度。
在这里查看我的答案: https ://stackoverflow.com/a/18585788/983912
编辑
我对您的示例项目进行了以下更改:
- (void)textViewDidBeginEditing:(UITextView *)textView
{
_caretVisibilityTimer = [NSTimer scheduledTimerWithTimeInterval:0.3 target:self selector:@selector(_scrollCaretToVisible) userInfo:nil repeats:YES];
}
- (void)_scrollCaretToVisible
{
//This is where the cursor is at.
CGRect caretRect = [self.textView caretRectForPosition:self.textView.selectedTextRange.end];
if(CGRectEqualToRect(caretRect, _oldRect))
return;
_oldRect = caretRect;
//This is the visible rect of the textview.
CGRect visibleRect = self.textView.bounds;
visibleRect.size.height -= (self.textView.contentInset.top + self.textView.contentInset.bottom);
visibleRect.origin.y = self.textView.contentOffset.y;
//We will scroll only if the caret falls outside of the visible rect.
if(!CGRectContainsRect(visibleRect, caretRect))
{
CGPoint newOffset = self.textView.contentOffset;
newOffset.y = MAX((caretRect.origin.y + caretRect.size.height) - visibleRect.size.height + 5, 0);
[self.textView setContentOffset:newOffset animated:NO];
}
}
首先删除了设置旧的插入符号位置,以及禁用的动画。现在似乎运作良好。
虽然@Divya 给出的答案引导我找到了正确的解决方案(所以我授予了赏金),但这并不是一个非常明确的答案!这里是详细的:
确保屏幕键盘不隐藏文本视图的标准方法是在显示键盘时更新其框架,如本问题所述:
但是,在 iOS 7 中,如果您在UIKeyboardWillShowNotification
通知处理程序中更改文本视图框架,则光标将保持在屏幕外,如本问题所述。
此问题的解决方法是更改文本视图框架以响应textViewDidBeginEditing
委托方法:
@implementation ViewController {
CGSize _keyboardSize;
UITextView* textView;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
textView = [[UITextView alloc] initWithFrame:CGRectInset(self.view.bounds, 20.0, 20.0)]; textView.delegate = self;
textView.returnKeyType = UIReturnKeyDone;
textView.backgroundColor = [UIColor greenColor];
textView.textColor = [UIColor blackColor];
[self.view addSubview:textView];
NSMutableString *textString = [NSMutableString new];
for (int i=0; i<100; i++) {
[textString appendString:@"cheese\rpizza\rchips\r"];
}
textView.text = textString;
}
- (void)textViewDidBeginEditing:(UITextView *)textView1 {
CGRect textViewFrame = CGRectInset(self.view.bounds, 20.0, 20.0);
textViewFrame.size.height -= 216;
textView.frame = textViewFrame;
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
CGRect textViewFrame = CGRectInset(self.view.bounds, 20.0, 20.0);
textView.frame = textViewFrame;
[textView endEditing:YES];
[super touchesBegan:touches withEvent:event];
}
@end
注意:不幸textViewDidBeginEdting
的是在通知之前触发UIKeyboardWillShowNotification
,因此需要对键盘高度进行硬编码。
以下对我有用:
.h 文件
@interface ViewController : UIViewController <UITextViewDelegate> {
UITextView *textView ;
}
@property(nonatomic,strong)IBOutlet UITextView *textView;
@end
.m 文件
@implementation ViewController
@synthesize textView;
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
CGRect textViewFrame = CGRectMake(20.0f, 20.0f, 280.0f, 424.0f);
//UITextView *textView = [[UITextView alloc] initWithFrame:textViewFrame];
textView.frame = textViewFrame;
textView.delegate = self;
textView.returnKeyType = UIReturnKeyDone;
textView.backgroundColor = [UIColor greenColor];
textView.textColor = [UIColor blackColor];
[self.view addSubview:textView];
}
- (BOOL)textViewShouldBeginEditing:(UITextView *)textView{
NSLog(@"textViewShouldBeginEditing:");
return YES;
}
- (void)textViewDidBeginEditing:(UITextView *)textView1 {
NSLog(@"textViewDidBeginEditing:");
CGRect textViewFrame = CGRectMake(20.0f, 20.0f, 280.0f, 224.0f);
textView1.frame = textViewFrame;
}
- (BOOL)textViewShouldEndEditing:(UITextView *)textView{
NSLog(@"textViewShouldEndEditing:");
return YES;
}
- (void)textViewDidEndEditing:(UITextView *)textView{
NSLog(@"textViewDidEndEditing:");
}
- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text{
return YES;
}
- (void)textViewDidChange:(UITextView *)textView{
NSLog(@"textViewDidChange:");
}
- (void)textViewDidChangeSelection:(UITextView *)textView{
NSLog(@"textViewDidChangeSelection:");
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
NSLog(@"touchesBegan:withEvent:");
CGRect textViewFrame = CGRectMake(20.0f, 20.0f, 280.0f, 424.0f);
textView.frame = textViewFrame;
[self.view endEditing:YES];
[super touchesBegan:touches withEvent:event];
}
@end
我已经完成了它和它的工作。
#define k_KEYBOARD_OFFSET 95.0
-(void)keyboardWillAppear {
// Move current view up / down with Animation
if (self.view.frame.origin.y >= 0)
{
[self moveViewUp:NO];
}
else if (self.view.frame.origin.y < 0)
{
[self moveViewUp:YES];
}
}
-(void)keyboardWillDisappear {
if (self.view.frame.origin.y >= 0)
{
[self moveViewUp:YES];
}
else if (self.view.frame.origin.y < 0)
{
[self moveViewUp:NO];
}
}
-(void)textFieldDidBeginEditing:(UITextField *)sender
{
//if ([sender isEqual:_txtPassword])
// {
//move the main view up, so the keyboard will not hide it.
if (self.view.frame.origin.y >= 0)
{
[self moveViewUp:YES];
}
//}
}
//Custom method to move the view up/down whenever the keyboard is appeared / disappeared
-(void)moveViewUp:(BOOL)bMovedUp
{
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.4]; // to slide the view up
CGRect rect = self.view.frame;
if (bMovedUp) {
// 1. move the origin of view up so that the text field will come above the keyboard
rect.origin.y -= k_KEYBOARD_OFFSET;
// 2. increase the height of the view to cover up the area behind the keyboard
rect.size.height += k_KEYBOARD_OFFSET;
} else {
// revert to normal state of the view.
rect.origin.y += k_KEYBOARD_OFFSET;
rect.size.height -= k_KEYBOARD_OFFSET;
}
self.view.frame = rect;
[UIView commitAnimations];
}
- (void)viewWillAppear:(BOOL)animated
{
// register keyboard notifications to appear / disappear the keyboard
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(keyboardWillAppear)
name:UIKeyboardWillShowNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(keyboardWillDisappear)
name:UIKeyboardWillHideNotification
object:nil];
}
- (void)viewWillDisappear:(BOOL)animated
{
// unregister for keyboard notifications while moving to the other screen.
[[NSNotificationCenter defaultCenter] removeObserver:self
name:UIKeyboardWillShowNotification
object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self
name:UIKeyboardWillHideNotification
object:nil];
}
这是我的解决方案,2015 年 7 月在针对 iOS 7.1 的 Xcode 6.4 上使用 Swift 1.2 - 多种方法的组合。借用Johnston 的键盘处理 Swift 代码。它有点像黑客,但它很简单而且很有效。
我在单个视图中有一个香草 UITextView。
根据Apple 的文档,我不想将它嵌入到 UIScrollView 中。我只想在软件键盘出现时重新调整 UITextView 的大小,并在关闭键盘时调整为原始大小。
这些是基本步骤:
所以,进入代码。
我已经通过界面构建器中的常用拖放在代码文件的顶部设置了约束出口:@IBOutlet weak var myUITextViewBottomConstraint: NSLayoutConstraint!
我还设置了一个全局变量,我可以在键盘出现之前备份事务状态:var myUITextViewBottomConstraintBackup: CGFloat = 0
实现键盘通知,在 viewDidLoad 或任何其他启动/设置部分调用此函数:
func setupKeyboardNotifications() {
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWasShown:"), name: UIKeyboardDidShowNotification, object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillBeHidden:"), name: UIKeyboardWillHideNotification, object: nil)
}
然后在显示/关闭键盘时会自动调用这两个函数:
func keyboardWasShown(aNotification:NSNotification) {
let info = aNotification.userInfo
let infoNSValue = info![UIKeyboardFrameBeginUserInfoKey] as! NSValue
let kbSize = infoNSValue.CGRectValue().size
let newHeight = kbSize.height
//backup old constraint size
myUITextViewBottomConstraintOld = myUITextViewBottomConstraint.constant
// I subtract 50 because otherwise it leaves a gap between keyboard and text view. I'm sure this could be improved on.
myUITextViewBottomConstraint.constant = newHeight - 50
func keyboardWillBeHidden(aNotification:NSNotification) {
//restore to whatever AutoLayout set it before you messed with it
myUITextViewBottomConstraint.constant = myUITextViewBottomConstraintOld
}
该代码有效,但有一个小问题:
@Johnston 找到了一个很好的解决方案。这是一个UIKeyboardWillChangeFrameNotification
正确解释键盘大小变化的变体(即显示/隐藏 QuickType 栏)。它还可以正确处理文本视图嵌入导航控制器的情况(即在contentInset
其他情况下不为零)。它也是用 Swift 2 编写的。
override func viewDidLoad() {
:
NSNotificationCenter.defaultCenter().addObserverForName(UIKeyboardWillChangeFrameNotification, object: nil, queue: nil) { (notification) -> Void in
guard let userInfo = notification.userInfo,
let keyboardFrameEndValue = userInfo[UIKeyboardFrameEndUserInfoKey] as? NSValue
else { return }
let windowCoordinatesKeyboardFrameEnd = keyboardFrameEndValue.CGRectValue() // window coordinates
let keyboardFrameEnd = self.view.convertRect(windowCoordinatesKeyboardFrameEnd, fromView: nil) // view coordinates
var inset = self.textView.contentInset
inset.bottom = CGRectGetMaxY(self.textView.frame) - CGRectGetMinY(keyboardFrameEnd) // bottom inset is the bottom of textView minus top of keyboard
self.textView.contentInset = inset
self.textView.scrollIndicatorInsets = inset
}
}