2011-05-11 10 views
7

Tengo una vista que es similar a la aplicación de notas, es decir, escribir en un papel rayado. Para hacer que el texto y el documento se desplacen simultáneamente, desactivé el desplazamiento de UITextView y, en su lugar, coloqué mi UITextView y mi UIImageView dentro de UIScrollView.¿Cómo puedo hacer que un desplazamiento UIScrollView sea una posición del cursor de UITextView?

El único problema con esto es que, cuando el usuario escribe, el texto desaparece debajo del teclado, porque obviamente el UIScrollView no sabe desplazarse a la posición del cursor.

¿Hay alguna manera simple de recuperar la posición del cursor y decirle al UIScrollView que se desplace allí?

--- --- EDITAR

partir de algo similar here (en la que alguien estaba tratando de hacer algo similar con un UITableView), he logrado hacer una creciente, UITextView editable con un fondo fijo que casi se desplaza perfectamente. Los únicos problemas ahora son:

  1. Se produce una ligera vibración a medida que el texto avanza si el usuario escribe de forma particularmente rápida.
  2. Si el usuario oculta el teclado, selecciona texto en la parte inferior de la pantalla y vuelve a mostrar el teclado, debe escribir un par de letras antes de volver a ser visible: no se desplaza hacia arriba inmediatamente.
  3. Cuando el usuario oculta el teclado, la animación como el marco de la vista de desplazamiento llena la pantalla no se siente del todo bien de alguna manera.

Este es el código - que estaría muy agradecido si alguien puede refinar aún más ...

#import "NoteEditViewController.h" 
#import "RLWideLabelTableCell.h" 

@implementation NoteEditViewController 
@synthesize keyboardSize; 
@synthesize keyboardHideDuration; 
@synthesize scrollView; 
@synthesize noteTextView; 

// 
// Dealloc and all that stuff 
// 
- (void)loadView 
{ 
    [super loadView]; 
    UIScrollView *aScrollView = [[UIScrollView alloc] initWithFrame:self.view.bounds]; 
    self.scrollView = aScrollView; [aScrollView release]; 
    self.scrollView.contentSize = CGSizeMake(self.view.frame.size.width, noteTextView.frame.size.height); 
    [self.view addSubview:scrollView]; 
} 

- (void)viewDidLoad 
{ 
    [super viewDidLoad]; 

    // Get notified when keyboard is shown. Don't need notification when hidden because we are 
    // using textViewDidEndEditing so we can start animating before the keyboard disappears. 
    [[NSNotificationCenter defaultCenter] addObserver:self 
              selector:@selector(keyboardWasShown:) 
               name:UIKeyboardDidShowNotification object:nil]; 

    // Add the Done button so we can test dismissal of the keyboard  
    UIBarButtonItem *doneButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone 
     target:self 
     action:@selector(doneButton:)]; 
    self.navigationItem.rightBarButtonItem = doneButton; [doneButton release]; 

    // Add the background image that will scroll with the text 
    CGRect noteImageFrame = CGRectMake(self.view.bounds.origin.x, 
             noteTitleImageFrame.size.height, 
             self.view.bounds.size.width, 500);  

    UIView *backgroundPattern = [[UIView alloc] initWithFrame:noteImageFrame]; 
    backgroundPattern.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:@"Notepaper-iPhone-Line"]]; 
    [self.scrollView addSubview:backgroundPattern]; 
    [self.view sendSubviewToBack:backgroundPattern]; 
    [backgroundPattern release]; 

    // Add the textView 
    CGRect textViewFrame = CGRectMake(noteImageFrame.origin.x+27, 
             noteImageFrame.origin.y-3, 
             noteImageFrame.size.width-35, 
             noteImageFrame.size.height); 

    RLTextView *textView = [[RLTextView alloc] initWithFrame:textViewFrame]; 
    self.noteTextView = textView; [textView release]; 
    self.noteTextView.font = [UIFont fontWithName:@"Cochin" size:21]; 
    self.noteTextView.backgroundColor = [UIColor clearColor]; 
    self.noteTextView.delegate = self; 
    self.noteTextView.scrollEnabled = NO; 
    [self.scrollView addSubview:self.noteTextView]; 
} 

- (void)doneButton:(id)sender 
{ 
    [self.view endEditing:TRUE]; 
} 

// When the keyboard is shown, the UIScrollView's frame shrinks so that it fits in the 
// remaining space 
- (void)keyboardWasShown:(NSNotification*)aNotification 
{ 
    NSDictionary* info = [aNotification userInfo]; 
    CGSize kbSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size; 
    float kbHideDuration = [[info objectForKey:UIKeyboardAnimationDurationUserInfoKey] floatValue]; 
    self.keyboardHideDuration = kbHideDuration; 
    self.keyboardSize = kbSize; 
    self.scrollView.frame = CGRectMake(self.view.bounds.origin.x, 
             self.view.bounds.origin.y, 
             self.view.bounds.size.width, 
             self.view.bounds.size.height - kbSize.height);  
} 

// When the user presses 'done' the UIScrollView expands to the size of its superview 
// again, as the keyboard disappears. 
- (void)textViewDidEndEditing:(UITextView *)textView 
{ 
    [UIScrollView animateWithDuration:keyboardHideDuration animations:^{self.scrollView.frame = self.view.bounds;}]; 
} 

// This method needs to get called whenever there is a change of cursor position in the text box 
// That means both textViewDidChange: and textViewDidChangeSelection: 
- (void)scrollToCursor 
{ 
    // if there is a selection cursor… 
    if(noteTextView.selectedRange.location != NSNotFound) { 
     NSLog(@"selectedRange: %d %d", noteTextView.selectedRange.location, noteTextView.selectedRange.length); 

     // work out how big the text view would be if the text only went up to the cursor 
     NSRange range; 
     range.location = noteTextView.selectedRange.location; 
     range.length = noteTextView.text.length - range.location; 
     NSString *string = [noteTextView.text stringByReplacingCharactersInRange:range withString:@""]; 
     CGSize size = [string sizeWithFont:noteTextView.font constrainedToSize:noteTextView.bounds.size lineBreakMode:UILineBreakModeWordWrap]; 

     // work out where that position would be relative to the textView's frame 
     CGRect viewRect = noteTextView.frame; 
     int scrollHeight = viewRect.origin.y + size.height; 
     CGRect finalRect = CGRectMake(1, scrollHeight, 1, 1); 

     // scroll to it 
     [self.scrollView scrollRectToVisible:finalRect animated:YES]; 
    } 
} 

// Whenever the text changes, the textView's size is updated (so it grows as more text 
// is added), and it also scrolls to the cursor. 
- (void)textViewDidChange:(UITextView *)textView 
{ 
    noteTextView.frame = CGRectMake(noteTextView.frame.origin.x, 
            noteTextView.frame.origin.y, 
            noteTextView.frame.size.width, 
            noteTextView.contentSize.height); 
    self.scrollView.contentSize = CGSizeMake(self.scrollView.contentSize.width, 
              noteTextView.frame.size.height+200); 
    [self scrollToCursor]; 
} 

// The textView scrolls to the cursor whenever the user changes the selection point. 
- (void)textViewDidChangeSelection:(UITextView *)aTextView 
{ 
    [self scrollToCursor]; 
} 

// PROBLEM - the textView does not scroll until the user starts typing - just selecting 
// it is not enough. 
- (void)textViewDidBeginEditing:(UITextView *)textView 
{ 
    [self scrollToCursor]; 
} 

Respuesta

5

¡Qué bueno que encontraste mi publicación al respecto, me alegro de que haya sido útil!

creo que puede que no esté viendo la línea de fondo, porque de esta línea:

CGRect finalRect = CGRectMake(1, scrollHeight, 1, 1); 

Estás creando un cuadro de punto de 1x1. Una sola línea de texto puede tener una altura de 20 o 30 puntos (dependiendo del tamaño de la fuente).Entonces, si está desplazando este punto a visible, puede que solo muestre el píxel superior de la línea inferior, lo que hace que la línea inferior sea efectivamente invisible. Si realiza finalRect un poco más alto de manera que cubra toda la línea, que podría funcionar mejor:

CGRect finalRect = CGRectMake(1, scrollHeight, 1, 30); 

Además, se le puede llamar a su código scrollRectToVisible varias veces a la vez, lo que puede provocar "sacudidas". En mi código, solo ejecuto scrollRectToVisible desde textViewDidChangeSelection y cambio el tamaño de UITextView (si es necesario) en textViewDidChange. UIScrollView (y por herencia UITableView) tiene soporte integrado para desplazar el elemento seleccionado activamente para que sea visible, lo que en mi prueba funcionó bien al simplemente redimensionar el UITextView mientras tipeaba (pero no al seleccionar un punto específico dentro con un toque).

+0

Gracias, Manesh! Da la casualidad de que acababa de cambiar los parámetros de FinalRect exactamente como dijiste, lo que hace que el texto se desplace más claramente. Pero el problema del "resultado final" al que me refiero es ligeramente diferente: si toco en la mitad inferior de la pantalla para hacer el textView activo, no se desplaza * en absoluto * cuando aparece el teclado - Tengo que escribir un letra o mover el cursor antes de los fuegos de desplazamiento. Supongo que esto se debe a que algo aún no está configurado cuando se llama a 'textViewDidBeginEditing', por lo que el método scrollToCursor no funciona. –

+0

¿Hay alguna posibilidad de que pueda publicar un proyecto de muestra con código de trabajo? Además, si quiere continuar intentando depurarlo usted mismo, consideraría poner declaraciones NSLog en cada función y ver en qué orden se activan, podría proporcionar alguna información. Algunas funciones pueden o no estar activadas dependiendo de cómo muestre y oculte el teclado. – Manesh

+0

Gracias Manesh - sucede que he encontrado una solución mucho más simple para todo el asunto. Como UITextView es una subclase de UIScrollView, puedo poner código en el método de delegado 'scrollViewDidScroll:' para mover la imagen de fondo. Así que solo tengo mi desplazamiento de UITextView de forma normal, y la vista de fondo separada se mueve en respuesta a él. De esta forma, el comportamiento de desplazamiento es perfecto, porque es el comportamiento incorporado de UITextView. ¡Gracias por toda la ayuda! –

1

No hay manera fácil de encontrar la pantalla coordenadas de cualquier texto o el cursor en una UITextView.

Lo que debe hacer es registrarse en UIKeyboardWillShowNotification y UIKeyboardWillShowNotification. Y en las devoluciones de llamadas, ajuste size o contentInsets del UIScrollView para ajustar el tamaño del teclado.

El tamaño del teclado, e incluso la duración de la animación se proporciona en las notificaciones userInfo, por lo que puede hacerlo de una manera muy animada.

a encontrar más información y código de ejemplo aquí: http://developer.apple.com/library/ios/#documentation/StringsTextFonts/Conceptual/TextAndWebiPhoneOS/KeyboardManagement/KeyboardManagement.html

Cuestiones relacionadas