2010-09-29 18 views
12

Tengo una UITextView encima de una UIView, y si la presiono para abrirla para editarla, el teclado está bloqueando la parte inferior de la vista y No puedo verlo aunque puedo escribir en esta área. ¿Puedo decirle a UITextView que tenga un área de desplazamiento diferente o cuál es la solución?¿Cómo puedo hacer que UITextView se desplace correctamente cuando el teclado está visible?

+1

duplicado mira aquí para la respuesta http://stackoverflow.com/questions/1126726/how-to -hacer-a-UITextField-mover-hasta-que-teclado-is-presente –

Respuesta

4

Finalmente lo tengo funcionando. Aquí está mi solución, ¿pueden detectar errores en mi diseño?

@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; 
}
+0

orientationAtShown ???? usted tiene listas de miembros de datos aquí sin declaraciones ni explicaciones – iOSProgrammingIsFun

4

Apple tiene algunos code samples que se ocupan de esta situación exacta.

+0

he implementado el ejemplo desde el enlace, y funciona, pero el área de desplazamiento para mi UITextView se convierte en un buen montón de pequeñas (como 50 píxeles). ¿Alguna idea de por qué? – Neigaard

+0

Comprueba las máscaras de cambio de tamaño en el Interface Builder. No estoy seguro de cuáles son las configuraciones correctas, pero controlan cómo responde la vista de texto a los cambios de tamaño de su elemento primario. –

+0

Cuando dices que cambiar el tamaño de la máscara, ¿a qué te refieres? – Neigaard

15

Una solución fácil es poner en práctica los UITextViewDelegate Métodos

- (void)textViewDidBeginEditing:(UITextView *)textView 

y

- (void)textViewDidEndEditing:(UITextView *)textView 

Puede hacer que el marco UITextView menor cuando aparece el teclado y que sea de tamaño completo de nuevo cuando el el teclado desaparece ... así:

- (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); 
} 

EDITAR

La solución anterior es malo ... no lo use !!!

Ahora creo que es una mejor idea cambiar el tamaño del UITextView en proporción al tamaño del teclado y no con un valor fijo ... porque el tamaño del teclado puede cambiar cuando se elige otro idioma o se gira el dispositivo. ..de supuesto -.-

al principio debe registrar su UIViewController que muestra su UITextView para recibir notificaciones de teclado:

- (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]; 
} 

entonces usted tiene que poner en práctica los dos métodos -keyboardWasShown: y -keyboardWillBeHidden:.

El tamaño del teclado real está contenido en el objeto 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); 
} 
+0

¡esta es la respuesta correcta! –

+1

No, no lo es. Ajuste contentInsets, no el marco. – Linasses

29

Una mejor solución, especialmente para iOS 7, habría que ajustar la propiedad de contenido inserción de la TextView en lugar de su marco, de esta manera, el teclado se desenfoque el texto que cae traseros que como en cualquier otro IOS 7 aplicación También deberá ajustar los indicadores de desplazamiento para que coincidan.

Ampliando la respuesta de Lindemann,

- (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; 
} 
+0

Gracias por esta respuesta. Esta absolutamente debe marcarse como la respuesta "correcta", sobre todo, como usted menciona, dados los cambios en iOS 7. En – Mani

+0

iOS 8, esto parece ocurrir de forma automática – adamF

+1

@adamF Esto no sucede automáticamente para mí en iOS 8 .. . – Jacob

4

@Alejandro anterior tiene la idea correcta, pero su código no funciona en modo horizontal.He modificado su método keyboardWasShown: para que funcione correctamente en todas las orientaciones:

- (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; 
    } 
} 
0

Extendiendo @alejandro & @Mani:

Th respuesta final:

- (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; 
} 
+0

esto no funciona tan bien, tal vez sea el problema UIKeyboardFrameEndUserInfoKey? – slboat

2

Añadir Observador por primera vez en 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]; 

} 

llamar a los métodos

- (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's 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; 
} 
1

si usted tiene más de 1 campo de texto o si desea reducir su código a continuación, tratar este código

- (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; 

} 
3

Si quieres una entrada de mensajes de estilo de aplicaciones , se puede utilizar un UITextView anidada (permite múltiples líneas de texto). Se parece a esto al final:

enter image description here

Se empieza por trazar una vista para contener todos los puntos de vista del niño. Aquí el color de fondo de la bottomView está ajustado para que coincida con UIKeyboardAppearanceDark. Descansa en la parte inferior de la pantalla.

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]; 

A continuación, agregue en una vista simple fondo de estilo como un UITextField típica, y añadir la UITextView como subvista a eso. El inputTV (UITextView) realiza su altura sobre la base del tamaño de la fuente. Además, todo el relleno se elimina de inputTV usando las variables 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]; 

En el ejemplo anterior, he incluido una etiqueta que indique cómo se dejan muchas cartas (caracteres máx/min) y un botón de envío.

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]; 

añadir esta línea al principio de su código, para que pueda obtener actualizaciones de cambios de teclado:

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil]; 

llama al método a continuación cuando un usuario hace clic en el inputTV. Aquí establece la variable 'keyboardHeight' utilizada más adelante.

-(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]; 
} 

Esta es la parte principal del código que se encarga de todo el movimiento y el cambio de tamaño de la 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 

        }]; 


} 

En el método anterior, esto es lo que está pasando:

  1. Si desea establecer un número mínimo o máximo de caracteres, puede hacerlo aquí. Apretar el número de caracteres y almacenar como un entero, y calcular cuántos caracteres quedan.

  2. Si el usuario ha alcanzado el número máximo de caracteres, restablezca el texto Textview por arrastre de nuevo a su máximo.

  3. Estos valores se utilizan para calcular la cantidad que necesita para mover su bottomView up y también para cambiar el tamaño de sus subvistas.

  4. Este método es solo para cambiar el color/texto de algunos elementos de la interfaz de usuario. No es estrictamente necesario.

  5. Este método trae a la vista lettersLeftLabel si está usando eso. Tampoco es necesario.

  6. Esto habilita el botón de enviar solo si se ha alcanzado el mínimo número de caracteres. Cambia el color como un indicador para el usuario.

  7. Si desea limitar el crecimiento de la entrada de TV y los elementos circundantes, puede incluir este código. Requiere que establezca la cantidad máxima de líneas que desea mostrar. Si el usuario excede el máximo, el desplazamiento se vuelve a activar para el inputTV, de lo contrario se establece de forma predeterminada en falso (importante para evitar que rebote).

  8. Esta es la lógica principal de cambio de tamaño, moviendo la parte inferiorVista y cambia el tamaño de sus vistas secundarias. El botón de envío debe permanecer en la misma posición, por lo que debes moverlo hacia abajo a medida que crece la vista inferior.

NOTA: Si lo que desea es el código de barebones, sólo es necesario para poner en práctica los pasos 3 y 8.

Cuestiones relacionadas