Подтвердить что ты не робот

Как заставить UITextView правильно прокручивать, когда клавиатура видна

У меня есть UITextView, сидящий поверх UIView, и если я нажимаю на него, чтобы открыть его для редактирования, то клавиатура блокирует нижнюю часть представления, и я не вижу его, хотя я могу писать в этой области, Могу ли я указать UITextView, чтобы иметь другую область прокрутки или каково решение?

4b9b3361

Ответ 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;
}