2011-01-14 20 views
13

Estoy escribiendo una aplicación para iPhone que (como la mayoría de las aplicaciones) admite rotación automática: rota el teléfono y sus vistas giran y cambian de tamaño de forma apropiada.iOS: titleView de la barra de navegación no cambia de tamaño correctamente cuando el teléfono gira

Pero estoy asignando una vista personalizada a navigationItem.titleView (el área del título de la barra de navegación), y no puedo obtener esa vista para cambiar el tamaño correctamente cuando el teléfono gira.

Sé lo que estás pensando: "Simplemente establece su autoresizingMask en UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight", pero no es tan simple. Por supuesto, si no configuro mi vista como autoresizingMask, entonces mi vista no cambia de tamaño; y quiero que cambie el tamaño.

El problema es que si I establece autoresizingMask, entonces cambia el tamaño correctamente siempre que esa vista esté visible; pero el tamaño titleView se ensucia en este escenario:

  1. Ejecute la aplicación, con el teléfono en modo retrato. Todo se ve bien.
  2. Haga algo que haga que la aplicación presione otra vista en la pila de navegación. P.ej. haga clic en una fila o botón de la tabla que causa una llamada al [self.navigationController pushViewController:someOtherViewController animated:YES].
  3. Mientras ve el controlador para niños, gire el teléfono a horizontal.
  4. Haga clic en el botón "Atrás" para volver a la vista de nivel superior. En este punto, la vista del título está en mal estado: aunque está sosteniendo el teléfono en modo horizontal, la vista del título todavía está dimensionada como si la estuviera sosteniendo en modo vertical.
  5. Finalmente, gire el teléfono nuevamente al modo vertical. Ahora las cosas empeoran: la vista del título se reduce de tamaño (ya que la barra de navegación se hizo más pequeña), pero como ya era demasiado pequeña, ahora es demasiado demasiado pequeña.

Si desea reproducir este usted mismo, siga estos pasos (esto es un poco de trabajo):

  1. hacer una aplicación utilizando el asistente "Aplicación basada en la navegación" de Xcode.
  2. Configúrelo de modo que la vista de tabla de nivel superior tenga filas que, al hacer clic en ellas, inserte una vista de detalles en la pila de navegación.
  3. incluir este código tanto en el controlador de vista de nivel superior y el controlador de la vista de detalle:

    - (BOOL)shouldAutorotateToInterfaceOrientation: 
         (UIInterfaceOrientation)interfaceOrientation { 
        return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown); 
    } 
    
  4. Incluir este código en sólo el controlador de vista de nivel superior:

    - (void)viewDidLoad { 
        [super viewDidLoad]; 
    
        // Create "Back" button 
        UIBarButtonItem *backButton = [[UIBarButtonItem alloc] initWithTitle:@"Master" 
         style:UIBarButtonItemStylePlain target:nil action:nil]; 
        self.navigationItem.backBarButtonItem = backButton; 
        [backButton release]; 
    
        // Create title view 
        UILabel* titleView = [[[UILabel alloc] initWithFrame:CGRectMake(0,0,500,38)] autorelease]; 
        titleView.textAlignment = UITextAlignmentCenter; 
        titleView.text = @"Watch this title view"; 
    
        // If I leave the following line turned on, then resizing of the title view 
        // messes up if I: 
        // 
        // 1. Start at the master view (which uses this title view) in portrait 
        // 2. Navigate to the detail view 
        // 3. Rotate the phone to landscape 
        // 4. Navigate back to the master view 
        // 5. Rotate the phone back to portrait 
        // 
        // On the other hand, if I remove the following line, then I get a different 
        // problem: The title view doesn't resize as I want it to when I: 
        // 
        // 1. Start at the master view (which uses this title view) in portrait 
        // 2. Rotate the phone to landscape 
        titleView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; 
    
        self.navigationItem.titleView = titleView; 
    } 
    
  5. Finalmente , sigue mis pasos de repro.

Entonces ... ¿estoy haciendo algo mal? ¿Hay alguna manera de hacer que mi titleView cambie de tamaño siempre correctamente?

Respuesta

2

(respondiendo a mi propia pregunta)

Tengo este trabajo al mantener un registro manual de los márgenes de la titleview (su distancia de los bordes de la barra Navigtion) - ahorro cuando la vista desaparece, y la restauración cuando la vista reaparece

La idea es que no estamos restaurando el titleView al tamaño exacto que tenía anteriormente; más bien, lo estamos restaurando para que tenga los mismos márgenes que tenía anteriormente. De esta forma, si el teléfono ha girado, titleView tendrá un tamaño nuevo y apropiado.

Aquí está mi código:

En archivo .h mi punto de vista del controlador:

@interface MyViewController ... 
{ 
    CGRect titleSuperviewBounds; 
    UIEdgeInsets titleViewMargins; 
} 

En el archivo .m mi punto de vista del controlador:

/** 
* Helper function: Given a parent view's bounds and a child view's frame, 
* calculate the margins of the child view. 
*/ 
- (UIEdgeInsets) calcMarginsFromParentBounds:(CGRect)parentBounds 
            childFrame:(CGRect)childFrame { 
    UIEdgeInsets margins; 
    margins.left = childFrame.origin.x; 
    margins.top = childFrame.origin.y; 
    margins.right = parentBounds.size.width - 
     (childFrame.origin.x + childFrame.size.width); 
    margins.bottom = parentBounds.size.height - 
     (childFrame.origin.y + childFrame.size.height); 
    return margins; 
} 

- (void)viewDidUnload { 
    [super viewDidUnload]; 

    titleSuperviewBounds = CGRectZero; 
    titleViewMargins = UIEdgeInsetsZero; 
} 

- (void) viewWillDisappear:(BOOL)animated { 
    [super viewWillDisappear:animated]; 

    // Keep track of bounds information, so that if the user changes the 
    // phone's orientation while we are in a different view, then when we 
    // return to this view, we can fix the titleView's size. 
    titleSuperviewBounds = self.navigationItem.titleView.superview.bounds; 
    CGRect titleViewFrame = self.navigationItem.titleView.frame; 
    titleViewMargins = [self calcMarginsFromParentBounds:titleSuperviewBounds 
               childFrame:titleViewFrame]; 
} 


- (void) viewDidAppear:(BOOL)animated { 
    [super viewDidAppear:animated]; 

    // Check for the case where the user went into a different view, then 
    // changed the phone's orientation, then returned to this view. In that 
    // case, our titleView probably has the wrong size, and we need to fix it. 
    if (titleSuperviewBounds.size.width > 0) { 
     CGRect newSuperviewBounds = 
      self.navigationItem.titleView.superview.bounds; 
     if (newSuperviewBounds.size.width > 0 && 
      !CGRectEqualToRect(titleSuperviewBounds, newSuperviewBounds)) 
     { 
      CGRect newFrame = UIEdgeInsetsInsetRect(newSuperviewBounds, 
       titleViewMargins); 
      newFrame.size.height = 
       self.navigationItem.titleView.frame.size.height; 
      newFrame.origin.y = floor((newSuperviewBounds.size.height - 
       self.navigationItem.titleView.frame.size.height)/2); 
      self.navigationItem.titleView.frame = newFrame; 
     } 
    } 
} 
+0

Tomó su código exactamente y me funciona muy bien, ¡gracias! – esilver

+0

Podría ser demasiado tarde (de 4 a 5 años de edad), pero ¿puedo preguntar ... en AutoLayout ... todavía te pasa esto de "no redimensionar"? Estoy trabajando con Storyboards y UINavigationControllers, cuando establezco un título realmente largo (digamos ... 100 caracteres o más), al rotar el iPhone funciona bien, pero en algún momento simplemente mantiene el tamaño del paisaje para la vista del título cuando rotado a retrato. No se que hacer. Sin personalización (arrastrando Navcontroller + controlador de vista raíz sin clases personalizadas) esto todavía está sucediendo. Me está volviendo loco, parece un error de iOS ... –

3

Tenía algo similar, pero volvía (reventado) al controlador de vista raíz. En última instancia, fui con lo siguiente para hacer estallar:

[[self navigationController] setNavigationBarHidden:YES animated:NO]; 
[[self navigationController] popViewControllerAnimated:YES]; 
[[self navigationController] setNavigationBarHidden:NO animated:NO]; 

Y funcionó.Pudo haber una manera mejor pero, después de todas las horas que ya había pasado en este tema, esto fue lo suficientemente bueno para mí.

0

Tuve el mismo problema, pero parece que tengo una solución con el siguiente código.

- (void)viewWillAppear:(BOOL)animated { 
    [super viewWillAppear:animated]; 

    UIView *urlField = self.navigationItem.leftBarButtonItem.customView; 
    CGRect frame = urlField.frame; 
    frame.size.width = 1000; 
    urlField.frame = frame; 
} 

En mi caso, la vista personalizada es un UITextField, pero espero que esto te ayude.

3

que se ocupó de este mismo problema hacer un seguimiento del marco inicial de la vista personalizada, luego alternar entre eso y una CGRect escalada de la trama inicial en un método -setLandscape en una subclase UIButton. Utilicé la subclase UIButton como navigationItem.titleView y navigationItem.rightBarButtonItem.

En UIButton subclase -

- (void)setLandscape:(BOOL)value 
{ 
    isLandscape = value; 

    CGFloat navbarPortraitHeight = 44; 
    CGFloat navbarLandscapeHeight = 32; 

    CGRect initialFrame = // your initial frame 
    CGFloat scaleFactor = floorf((navbarLandscapeHeight/navbarPortraitHeight) * 100)/100; 

    if (isLandscape) { 
     self.frame = CGRectApplyAffineTransform(initialFrame, CGAffineTransformMakeScale(scaleFactor, scaleFactor)); 
    } else { 
     self.frame = initialFrame; 
    } 
} 

Luego, en los delegados InterfaceOrientation que invoca el método -setLandscape en los CustomViews para cambiar sus tamaños.

En UIViewController -

- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration 
{ 
    [self updateNavbarButtonsToDeviceOrientation];; 
} 

- (void)updateNavbarButtonsToDeviceOrientation 
{ 
    ResizeButton *rightButton = (ResizeButton *)self.navigationItem.rightBarButtonItem.customView; 
    ResizeButton *titleView = (ResizeButton *)self.navigationItem.titleView; 

    if (self.interfaceOrientation == UIDeviceOrientationPortrait || self.interfaceOrientation == UIDeviceOrientationPortraitUpsideDown) { 
     [rightButton setLandscape:NO]; 
     [titleView setLandscape:NO]; 
    } else { 
     [rightButton setLandscape:YES]; 
     [titleView setLandscape:YES]; 
    } 
} 
7

También debe establecer el contentMode del UIImageView para obtener el titleView correctamente visualizado en el paisaje y/o el modo de retrato:

imgView.contentMode=UIViewContentModeScaleAspectFit;

El conjunto secuencia: (self es una instancia de UIViewController)

UIImageView* imgView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"myCustomTitle.png"]]; 
imgView.autoresizingMask=UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth; 
imgView.contentMode=UIViewContentModeScaleAspectFit; 
self.navigationItem.titleView = imgView; 
[imgView release]; 
2

Para IOS5 en adelante, ya que esta es una vieja pregunta ... Así es como logré el mismo problema con el texto del título que no se alineaba correctamente.

[[UINavigationBar appearance] setTitleVerticalPositionAdjustment:2 forBarMetrics:UIBarMetricsLandscapePhone]; 

Probado en ios5/6 sims funciona bien.

+0

Esta única línea resolvió mi problema. Perfecta para iOS 5+ – LightMan

1

Esto es lo que hice:

self.viewTitle.frame = self.navigationController.navigationBar.frame; 
self.navigationItem.titleView = self.viewTitle; 

El VIEWTITLE es una vista creada en el XI ter, se necesita el tamaño de la navigationbar y después de que se ha añadido el titleview ajustar el tamaño de dejar espacio a la botón de retroceso. Las rotaciones parecen funcionar bien.

Cuestiones relacionadas