Как изменить размер UITextView, когда клавиатура показана с iOS 7
У меня есть контроллер вида, который содержит полноэкранный UITextView
. Когда отображается клавиатура, я хотел бы изменить размер текстового представления, чтобы он не был скрыт под клавиатурой.
Это довольно стандартный подход с iOS, как описано в этом вопросе:
Как изменить размер UITextView на iOS при появлении клавиатуры?
Однако, с iOS 7, если пользователь вступает в текстовое представление в нижней половине экрана, при изменении размера текста курсор остается вне экрана. Текстовое представление только прокручивает, чтобы отобразить курсор, если при входе пользователя нажать.
Ответы
Ответ 1
В то время как ответ, данный @Divya, приводит меня к правильному решению (поэтому я наградил щедростью), это не очень ясный ответ! Здесь он подробно:
Стандартный подход к обеспечению того, чтобы текстовое представление не скрывалось экранной клавиатурой, заключается в обновлении его кадра при отображении клавиатуры, как подробно описано в этом вопросе:
Как изменить размер UITextView на iOS при появлении клавиатуры?
Однако с 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
, поэтому необходимо жестко закодировать высоту клавиатуры.
Ответ 2
Я прочитал документы, которые говорят об этой теме. Я перевел его в Свифт, и он отлично работал у меня.
Это используется для полного UITextView страницы, такого как iMessage.
Я использую iOS 8.2 и Swift на XCode 6.2, и здесь мой код. Просто позвоните 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)
}
Swift 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
}
Ответ 3
С помощью Auto Layout намного проще (если вы понимаете Auto Layout):
Вместо того, чтобы пытаться идентифицировать и изменять размер затронутых представлений, вы просто создаете родительский фрейм для всего содержимого вашего представления. Затем, если появится kbd, вы измените размер кадра, и если вы правильно настроили ограничения, представление будет хорошо упорядочивать все его дочерние элементы. Не нужно возиться с большим количеством трудночитаемого кода для этого.
Фактически, в подобном вопросе я нашел ссылку на этот отличный учебник об этой технике.
Кроме того, другие примеры здесь, которые используют textViewDidBeginEditing вместо UIKeyboardWillShowNotification, имеют одну большую проблему:
Если у пользователя есть внешняя клавиатура Bluetooth, тогда элемент управления все равно будет нажат, даже если экранная клавиатура не появится. Это нехорошо.
Итак, суммируем:
- Использовать автоматический макет
- Используйте уведомление UIKeyboardWillShowNotification,
а не события TextEditField для принятия решения о том, когда
просмотров.
В качестве альтернативы, проверьте ответ LeoNatan. Это может быть даже более чистым и простым решением (я еще не пробовал себя).
Ответ 4
Не изменять размер текстового представления. Вместо этого установите дно contentInset
и scrollIndicatorInsets
на высоту клавиатуры.
См. мой ответ здесь:
fooobar.com/questions/102966/...
Изменить
В ваш образец проекта были внесены следующие изменения:
- (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];
}
}
Сначала удалена настройка старой позиции каретки, а также отключена анимация. Теперь, похоже, хорошо работает.
Ответ 5
Следующее работает для меня:
.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
Ответ 6
Я сделал это и его работу полностью.
#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];
}
Ответ 7
Это мое решение, июль 2015 года, с использованием Swift 1.2 на Xcode 6.4, ориентированного на iOS 7.1 - комбинация нескольких подходов. Заемная клавиатура Johnston, передающая Swift-код. Его немного взломать, но его просто и он работает.
У меня есть UITextView ванили в одном представлении.
Я не хотел встроить его в UIScrollView в соответствии с документацией Apple. Я просто хотел, чтобы UITextView изменился, когда появилась клавиатура программного обеспечения, и изменилась на оригинал, когда клавиатура была уволена.
Это основные шаги:
- Настройка уведомлений клавиатуры
- Установите ограничение компоновки в "Interface Builder" (TextView до нижнего края в моем случае)
- Создайте IBOutlet для этого ограничения в соответствующем файле кода, чтобы вы могли его программно настроить
- Использовать уведомления о клавиатуре для перехвата событий и получения размера клавиатуры.
- Программно настроить ограничение IBOutlet с использованием размера клавиатуры для изменения размера TextView.
- Поместите все обратно, когда клавиатура отклоняется.
Итак, на код.
Я установил выход ограничения в верхней части файла кода с помощью обычного перетаскивания в конструкторе интерфейса: @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
}
Код работает с незначительной проблемой:
- Он не реагирует на прогностическую текстовую ленту над клавиатурой, открывая/закрывая. То есть он будет учитывать его состояние при вызове клавиатуры, но если вы должны сдвигать его вверх или вниз, пока отображается клавиатура, ограничение не будет изменено. Это отдельное событие, которое необходимо обработать. Недостаточно функциональности для меня, чтобы беспокоиться.
Ответ 8
@Джонстон нашел хорошее решение. Здесь изменяется вариант с использованием 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
}
}