У меня есть UITextView, сидящий поверх UIView, и если я нажимаю на него, чтобы открыть его для редактирования, то клавиатура блокирует нижнюю часть представления, и я не вижу его, хотя я могу писать в этой области, Могу ли я указать UITextView, чтобы иметь другую область прокрутки или каково решение?
Как заставить UITextView правильно прокручивать, когда клавиатура видна
Ответ 1
Наконец-то я начал работать. Вот мое решение, можете ли вы, ребята, заметить какие-либо ошибки в моем дизайне?
@synthesize textView = _textView;
@synthesize callbackViewController = _callbackViewController;
-(void)keyboardWasShown:(NSNotification*)aNotification {
if(keyboardShown) {
return;
}
NSDictionary *info = [aNotification userInfo];
// Get the size of the keyboard.
NSValue *aValue = [info objectForKey:UIKeyboardFrameBeginUserInfoKey];
keyboardSize = [aValue CGRectValue].size;
// Resize the scroll view (which is the root view of the window)
CGRect viewFrame = [self.textView frame];
orientationAtShown = orientation;
if(orientation == UIInterfaceOrientationPortrait || orientation == UIInterfaceOrientationPortraitUpsideDown) {
viewFrame.size.height -= keyboardSize.height;
} else {
viewFrame.size.height -= keyboardSize.width;
}
self.textView.frame = viewFrame;
// Scroll the active text field into view.
//CGRect textFieldRect = [activeField frame];
[self.textView scrollRectToVisible:viewFrame animated:YES];
keyboardShown = YES;
}
-(void)keyboardWasHidden:(NSNotification*)aNotification {
if(!keyboardShown) {
return;
}
// Reset the height of the scroll view to its original value
CGRect viewFrame = [self.textView frame];
if(orientationAtShown == UIInterfaceOrientationPortrait || orientationAtShown == UIInterfaceOrientationPortraitUpsideDown) {
viewFrame.size.height += keyboardSize.height;
} else {
viewFrame.size.height += keyboardSize.width;
}
self.textView.frame = viewFrame;
keyboardShown = NO;
}
-(void)registerForKeyboardNotifications {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(keyboardWasShown:)
name:UIKeyboardDidShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(keyboardWasHidden:)
name:UIKeyboardDidHideNotification object:nil];
}
-(void)viewWillAppear:(BOOL)animated {
keyboardShown = NO;
[self registerForKeyboardNotifications];
}
-(void)viewWillDisappear:(BOOL)animated {
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil];
}
// Override to allow orientations other than the default portrait orientation.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
if(keyboardShown) {
[self keyboardWasHidden:nil];
}
orientation = interfaceOrientation;
CGRect viewFrame = [self.textView frame];
if(orientation == UIInterfaceOrientationPortrait || orientation == UIInterfaceOrientationPortraitUpsideDown) {
if(viewFrame.size.width > viewFrame.size.height) {
CGRect viewFrameFixed = CGRectMake(viewFrame.origin.x, viewFrame.origin.y, viewFrame.size.height, viewFrame.size.width);
self.textView.frame = viewFrameFixed;
}
} else {
if(viewFrame.size.width < viewFrame.size.height) {
CGRect viewFrameFixed = CGRectMake(viewFrame.origin.x, viewFrame.origin.y, viewFrame.size.height, viewFrame.size.width);
self.textView.frame = viewFrameFixed;
}
}
// Return YES for supported orientations
return YES;
}
Ответ 2
Лучшим решением, специально для iOS 7, было бы отредактировать свойство вставки содержимого текстового поля вместо его кадра, таким образом, клавиатура размывает текст, который отстает от него, как в любом другом приложении iOS 7. Вам также придется настроить индикаторы прокрутки, чтобы они соответствовали.
Расширение ответа Линдемана,
- (void)keyboardWasShown:(NSNotification*)notification {
NSDictionary* info = [notification userInfo];
CGSize keyboardSize = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size;
self.textView.contentInset = UIEdgeInsetsMake(0, 0, keyboardSize.height, 0);
self.textView.scrollIndicatorInsets = self.textView.contentInset;
}
- (void)keyboardWillBeHidden:(NSNotification*)notification {
self.textView.contentInset = UIEdgeInsetsZero;
self.textView.scrollIndicatorInsets = UIEdgeInsetsZero;
}
Ответ 3
Простым решением является реализация методов UITextViewDelegate
- (void)textViewDidBeginEditing:(UITextView *)textView
и
- (void)textViewDidEndEditing:(UITextView *)textView
Вы можете сделать UITextView Frame меньше при появлении клавиатуры и снова сделать его полным размером, когда клавиатура исчезнет... например:
- (void)textViewDidBeginEditing:(UITextView *)textView {
self.textView.frame = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height/1.8);
}
- (void)textViewDidEndEditing:(UITextView *)textView {
self.textView.frame = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height);
}
ИЗМЕНИТЬ
Решение выше плохое... не используйте его!!!
Теперь я думаю, что лучше изменить размер UITextView пропорционально размеру клавиатуры, а не фиксированному значению... потому что размер клавиатуры может измениться при выборе другого языка или при повороте устройства. Конечно -.-
Сначала вы должны зарегистрировать свой UIViewController
, который отобразит ваш UITextView
для получения уведомлений о клавиатуре:
- (void)viewDidLoad {
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(keyboardWasShown:)
name:UIKeyboardDidShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(keyboardWillBeHidden:)
name:UIKeyboardWillHideNotification object:nil];
}
Затем вам нужно реализовать два метода -keyboardWasShown:
и
-keyboardWillBeHidden:
.
Размер фактической клавиатуры содержится в объекте NSNotification
.
- (void)keyboardWasShown:(NSNotification*)notification {
NSDictionary* info = [notification userInfo];
CGSize keyboardSize = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size;
self.textView.frame = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height - keyboardSize.height);
}
- (void)keyboardWillBeHidden:(NSNotification*)notification {
self.textView.frame = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height);
}
Ответ 4
В Apple есть некоторые примеры кода, которые касаются этой точной ситуации.
Ответ 5
@Alejandro выше имеет правильную идею, но его код не работает в ландшафтном режиме. Я применил его метод keyboardWasShown:
для правильной работы во всех ориентациях:
- (void)keyboardWasShown:(NSNotification *)notification {
if (self.textView != nil) {
NSDictionary* info = [notification userInfo];
CGRect keyboardRect = [self.textView convertRect:[[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue] fromView:nil];
CGSize keyboardSize = keyboardRect.size;
self.textView.contentInset = UIEdgeInsetsMake(0, 0, keyboardSize.height, 0);
self.textView.scrollIndicatorInsets = self.textView.contentInset;
}
}
Ответ 6
Если вы хотите ввести стиль приложения Message App, вы можете использовать вложенный UITextView (допускается несколько строк текста). В конце он будет выглядеть следующим образом:
Сначала вы создаете представление, чтобы удерживать все дочерние представления. Здесь фоновый цвет bottomView установлен в соответствии с UIKeyboardAppearanceDark. Он располагается внизу экрана.
bottomView = [UIView new];
bottomView.frame = CGRectMake(0, h-45, w, 45);
bottomView.backgroundColor = [UIColor colorWithRed:0.078 green:0.078 blue:0.078 alpha:1];
[self.view addSubview:bottomView];
Затем добавьте простой вид фона, подобный типичному UITextField, и добавьте UITextView в качестве подзадачи к этому. Входной ТВ (UITextView) занимает высоту, основанную на размере шрифта. Кроме того, все дополнения удаляются из inputTV с использованием переменных textContainer.
inputTVBG = [UIImageView new];
inputTVBG.frame = CGRectMake(10, 8, w-90, 29);
inputTVBG.backgroundColor = [[UIColor whiteColor] colorWithAlphaComponent:0.1f];
inputTVBG.layer.cornerRadius = 4.0f;
inputTVBG.userInteractionEnabled = true;
inputTVBG.clipsToBounds = true;
[bottomView addSubview:inputTVBG];
inputTV = [UITextView new];
inputTV.font = [UIFont systemFontOfSize:14.0f];
inputTV.frame = CGRectMake(5, 6, w-100, inputTV.font.lineHeight);
inputTV.backgroundColor = [UIColor clearColor];
inputTV.keyboardAppearance = UIKeyboardAppearanceDark;
inputTV.delegate = self;
inputTV.autocorrectionType = UITextAutocorrectionTypeNo;
inputTV.tintColor = [UIColor whiteColor];
inputTV.textColor = [UIColor whiteColor];
inputTV.textContainer.lineFragmentPadding = 0;
inputTV.textContainerInset = UIEdgeInsetsZero;
[inputTVBG addSubview:inputTV];
В приведенном выше примере я включил метку, указывающую, сколько букв осталось (символы max/min) и кнопка отправки.
lettersLeftLabel = [UILabel new];
lettersLeftLabel.frame = CGRectMake(w-70, 8, 60, 16);
lettersLeftLabel.font = [UIFont systemFontOfSize:12.0f];
lettersLeftLabel.textColor = [[UIColor whiteColor] colorWithAlphaComponent:0.5f];
lettersLeftLabel.alpha = 0.0f;
[bottomView addSubview:lettersLeftLabel];
submitButton = [UIButton new];
submitButton.frame = CGRectMake(w-70, 0, 60, 45);
[submitButton setTitle:@"SUBMIT" forState:UIControlStateNormal];
[submitButton setTitleColor:[_peacock.applePink colorWithAlphaComponent:0.5f] forState:UIControlStateNormal];
[submitButton addTarget:self action:@selector(submit) forControlEvents:UIControlEventTouchUpInside];
[submitButton.titleLabel setFont:[UIFont boldSystemFontOfSize:14.0f]];
[bottomView addSubview:submitButton];
Добавьте эту строку на раннем этапе в свой код, чтобы получить обновления смены клавиатуры:
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
Он вызывает метод ниже, когда пользователь нажимает на входной ТВ. Здесь он устанавливает переменную "keyboardHeight", которая используется позже.
-(void)keyboardWillShow:(NSNotification *)n {
CGRect rect = [n.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
CGRect keyboardFrame = [self.view convertRect:rect fromView:nil];
keyboardHeight = keyboardFrame.size.height;
[self textViewDidChange:inputTV];
}
Это основной бит кода, который заботится обо всех перемещениях и изменении размера входного ТВ.
-(void)textViewDidChange:(UITextView *)textView {
//1. letters and submit button vars
int numberOfCharacters = (int)textView.text.length;
int minCharacters = 50;
int maxCharacters = 400;
int remainingCharacters = maxCharacters-numberOfCharacters;
//2. if entered letters exceeds maximum, reset text and return
if (remainingCharacters <= 0){
textView.text = [textView.text substringToIndex:maxCharacters];
numberOfCharacters = maxCharacters;
}
//3. set height vars
inputTV.scrollEnabled = true;
float textHeight = textView.contentSize.height;
float lineHeight = roundf(textView.font.lineHeight);
float additionalHeight = textHeight - lineHeight;
float moveUpHeight = keyboardHeight + additionalHeight;
//4. default letter colour is weak white
UIColor * letterColour = [[UIColor whiteColor] colorWithAlphaComponent:0.5f];
if (numberOfCharacters < minCharacters){ //minimum threshold not met
lettersLeftLabel.text = [NSString stringWithFormat:@"%i", minCharacters-numberOfCharacters];
letterColour = [_peacock.applePink colorWithAlphaComponent:0.5f];
} else { //within range
lettersLeftLabel.text = [NSString stringWithFormat:@"%i/%i", numberOfCharacters, maxCharacters];
if (remainingCharacters<5){ //increase alpha towards the end of range
letterColour = [[UIColor whiteColor] colorWithAlphaComponent:1.0f - ((float)remainingCharacters/10)];
}
}
//5. hide/show letter label based on textView height
float letterAlpha = 0.0f; //default hide
if (additionalHeight > 0){ letterAlpha = 1.0f; } //if multiline, show
[UIView animateWithDuration:0.3f
delay:0.0f
options:UIViewAnimationOptionCurveEaseOut
animations:^{
lettersLeftLabel.alpha = letterAlpha;
lettersLeftLabel.textColor = letterColour;
}
completion:^(BOOL finished){
}];
//6. update submit colour based on minimum threshold
UIColor * submitColour = [_peacock.applePink colorWithAlphaComponent:0.5f];
bool enableSubmit = false;
if (numberOfCharacters >= minCharacters){
submitColour = _peacock.applePink;
enableSubmit = true;
}
[submitButton setEnabled:enableSubmit];
[UIView animateWithDuration:0.3f
delay:0.0f
options:UIViewAnimationOptionCurveEaseOut
animations:^{
[submitButton setTitleColor:submitColour forState:UIControlStateNormal];
}
completion:^(BOOL finished){
}];
//7. special case if you want to limit the frame size of the input TV to a specific number of lines
bool shouldEnableScroll = false;
int maxNumberOfLines = 5; //anything above this triggers the input TV to stay stationary and update its scroll
int actualNumberOfLines = textHeight / textView.font.lineHeight;
if (actualNumberOfLines >= maxNumberOfLines){ //recalculate vars for frames
textHeight = maxNumberOfLines * lineHeight;
additionalHeight = textHeight - lineHeight;
moveUpHeight = keyboardHeight + additionalHeight;
shouldEnableScroll = true;
}
//8. adjust frames of views
inputTV.frame = CGRectMake(5, 6, w-100, textHeight); //update immediately (parent view clips to bounds)
[UIView animateWithDuration:0.3f
delay:0.0f
options:UIViewAnimationOptionCurveEaseOut
animations:^{
bottomView.frame = CGRectMake(0, h-45-moveUpHeight, w, 45+additionalHeight);
inputTVBG.frame = CGRectMake(10, 8, w-90, lineHeight+additionalHeight+13);
submitButton.frame = CGRectMake(w-70, additionalHeight, 60, 45);
}
completion:^(BOOL finished){
inputTV.scrollEnabled = shouldEnableScroll; //default disable scroll here to avoid bouncing
}];
}
В приведенном выше методе это происходит:
-
Если вы хотите установить минимальное или максимальное количество символов, вы можете сделать это здесь. Вы вытягиваете количество символов и сохраняете как целое число, и вычисляете, сколько символов осталось.
-
Если пользователь достиг максимального количества символов, reset текст textView, отменив его до макс.
-
Эти вары используются для расчета того, сколько вам нужно переместить снизу вверх, а также для изменения размера его подзонов.
-
Этот метод предназначен только для изменения цвета/текста некоторых элементов пользовательского интерфейса. Это не обязательно.
-
Этот метод выводит символы letterLeftLabel на просмотр, если вы используете это. Это также не нужно.
-
Это позволяет кнопке отправки только при достижении минимального количества символов. Он изменяет цвет в качестве индикатора для пользователя.
-
Если вы хотите ограничить рост входного ТВ и окружающих элементов, вы можете включить этот бит кода. Для этого требуется установить максимальное количество строк, которые вы хотите отобразить. Если пользователь превышает максимальный, прокрутка повторно используется для входного ТВ, в противном случае по умолчанию используется значение false (важно остановить его подпрыгивание).
-
Это основная логика изменения размера, перемещение bottomView вверх и изменение размера его дочерних представлений. Кнопка отправки должна оставаться в том же положении, поэтому перемещайте ее вниз по мере роста нижнего экрана.
ПРИМЕЧАНИЕ. Если вам нужен только код barebone, вам нужно всего лишь выполнить шаги 3 и 8.
Ответ 7
Добавить Observer сначала в viewDidLoad
.
- (void)viewDidLoad {
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(keyboardWasShown:)
name:UIKeyboardDidShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(keyboardWasHidden:)
name:UIKeyboardWillHideNotification object:nil];
}
Вызвать методы
- (void)keyboardWasShown:(NSNotification*)aNotification
{
NSDictionary* info = [aNotification userInfo];
CGSize kbSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, kbSize.height, 0.0);
self.textView.contentInset = contentInsets;
self.textView.scrollIndicatorInsets = contentInsets;
// If active text field is hidden by keyboard, scroll it so it visible
// Your app might not need or want this behavior.
CGRect aRect = self.view.frame;
aRect.size.height -= kbSize.height;
if (!CGRectContainsPoint(aRect, self.textView.frame.origin) ) {
[self.textView scrollRectToVisible:self.textView.frame animated:YES];
}
}
// Called when the UIKeyboardWillHideNotification is sent
- (void)keyboardWasHidden:(NSNotification*)aNotification
{
UIEdgeInsets contentInsets = UIEdgeInsetsZero;
self.textView.contentInset = contentInsets;
self.textView.scrollIndicatorInsets = contentInsets;
}
Ответ 8
Если у вас есть более 1 текстовое поле или вы хотите уменьшить свой код, попробуйте этот код
- (void)textFieldDidBeginEditing:(UITextField *)textField{
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.35f];
CGRect frame = self.view.frame;
frame.origin.y = (self.view.frame.size.height - textField.frame.origin.y) - self.view.frame.size.height+60;
if (frame.origin.y<-162) {
frame.origin.y = -162;
}
[self.view setFrame:frame];
[UIView commitAnimations];
}
-(BOOL)textFieldShouldEndEditing:(UITextField *)textField{
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.35f];
CGRect frame = self.view.frame;
frame.origin.y = 0;
[self.view setFrame:frame];
[UIView commitAnimations];
return YES;
}
Ответ 9
Расширение @alejandro и @Mani:
Последний ответ:
- (void)keyboardWasShown:(NSNotification *)notification {
if (self.textView != nil) {
NSDictionary* info = [notification userInfo];
CGRect keyboardRect = [self.textNote convertRect:[[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue] fromView:nil];
CGSize keyboardSize = keyboardRect.size;
self.textView.contentInset = UIEdgeInsetsMake(0, 0, keyboardSize.height, 0);
self.textView.scrollIndicatorInsets = self.textView.contentInset;
}
}
- (void)keyboardWillBeHidden:(NSNotification*)notification {
self.textView.scrollIndicatorInsets = UIEdgeInsetsZero;
self.textView.scrollIndicatorInsets = UIEdgeInsetsZero;
}