2011-01-14 8 views
13

Я пишу приложение для iPhone, которое (как и большинство приложений) поддерживает автоматическое вращение: вы поворачиваете свой телефон, и его виды вращаются и изменяются соответствующим образом.iOS: название панели навигации не изменяется правильно, когда телефон вращается

Но я назначаю пользовательский вид navigationItem.titleView (область заголовка панели навигации), и я не могу получить это представление для правильного изменения размера, когда телефон вращается.

Я знаю, что вы думаете: «Просто установите его autoresizingMask на номер UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight», но это не так просто. Конечно, если я не установлю свой вид autoresizingMask, то мое представление не изменится; и я хочу, чтобы он изменил размер.

Проблема в том, что если я сделал, установите его autoresizingMask, после чего он будет правильно изменен, пока вид будет виден; но размер titleView в этом случае перепутался:

  1. Запуск приложения с телефоном в портретном режиме. Все выглядит хорошо.
  2. Сделайте что-то, что заставляет приложение нажать другой вид на стек навигации. Например. щелкните строку или кнопку таблицы, которая вызывает вызов [self.navigationController pushViewController:someOtherViewController animated:YES].
  3. При просмотре детского контроллера поверните телефон в альбомную.
  4. Нажмите кнопку «Назад», чтобы вернуться к просмотру верхнего уровня. На этом этапе вид заголовка перепутался: хотя вы держите телефон в альбомном режиме, название заголовка по-прежнему имеет размер, как если бы вы держали его в портретном режиме.
  5. Наконец, поверните телефон обратно в портретный режим. Теперь ситуация становится еще хуже: название названия сокращается в размере (поскольку панель навигации уменьшилась), но поскольку она уже была слишком маленькой, теперь это много слишком мало.

Если вы хотите воспроизвести это самостоятельно, выполните следующие действия (это немного работы):

  1. сделать приложение с помощью «Навигация на основе приложений» мастера Xcode в.
  2. Установите его так, чтобы в верхнем уровне таблицы были строки, которые при нажатии на них нажимают подробный вид на стек навигации.
  3. Включите этот код в обоих контроллере представления верхнего уровня и контроллер представления детали:

    - (BOOL)shouldAutorotateToInterfaceOrientation: 
         (UIInterfaceOrientation)interfaceOrientation { 
        return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown); 
    } 
    
  4. включить этот код в только контроллере представления верхнего уровня:

    - (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. Наконец , следуйте моим инструкциям по воспроизведению.

Итак ... я делаю что-то неправильно? Есть ли способ сделать мой TitleView всегда правильно измененным?

ответ

2

(Ответ на мой собственный вопрос)

Я получил эту работу вручную отслеживать показатели рентабельности titleView (ее расстояние от края панели navigtion) - экономия, когда представление исчезает, и восстановление, когда вид появляется снова.

Идея заключается в том, что мы не восстанавливаем titleView до точного размера, который был ранее; скорее, мы восстанавливаем его так, чтобы он имел те же поля, которые были ранее. Таким образом, если телефон повернут, заголовок будет иметь новый, соответствующий размер.

Вот мой код:

В файле .h мой взгляд контроллера:

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

В файле .m мой взгляд контроллера:

/** 
* 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

Взял ваш код точно, и он отлично работает для меня, спасибо! – esilver

+0

Возможно, я опоздал (4-5 лет LOL), но могу ли я спросить ... в AutoLayout ... это все еще не изменилось? Я работаю со Storiesboards и UINavigationControllers, когда я устанавливаю действительно длинный заголовок (скажем ... 100 символов и более), при повороте iPhone он работает хорошо, но в какой-то момент он просто сохраняет размер ландшафта для заголовка, когда поворачивается на портрет. Я не знаю, что делать. Без персонализации (перетаскивание контроллера Navcontroller + Root view без пользовательских классов) это все еще происходит. Это сводит меня с ума, похоже на ошибку iOS ... –

3

У меня было что-то похожее, но оно возвращалось в контроллер корневого представления. В конечном счете, я пошел со следующим для высказывания:

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

И это сработало.Возможно, был лучший способ, но - после всех часов, которые я уже потратил на эту проблему, это было достаточно для меня.

0

У меня была такая же проблема, но я, похоже, обходился с последующим кодом.

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

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

В моем случае пользовательский вид UITextField, но я надеюсь, что это вам поможет.

3

Я имел дело с этим же вопросу отслеживая начальный кадр customView, а затем переключаясь между этим и масштабированным CGRect исходного кадра в методе -setLandscape в подклассе UIButton. Я использовал подкласс UIButton как navigationItem.titleView и navigationItem.rightBarButtonItem.

В UIButton подкласса -

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

Тогда в делегатов InterfaceOrientation я вызывается метод -setLandscape на customViews изменить их размеры.

В 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

Вы также должны установить contentMode в UIImageView, чтобы получить titleView правильно отображаться в портретном режиме ландшафта и/или:

imgView.contentMode=UIViewContentModeScaleAspectFit;

Вся последовательность: (self - пример UIViewController)

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

Для IOS5 вперед, поскольку это старый вопрос ... Вот как я справился с той же проблемой, что и текст заголовка, который не правильно выравнивается.

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

Протестировано на ios5/6 sims отлично работает.

+0

Эта единственная строка решла мою проблему. Прекрасно подходит для iOS 5+ – LightMan

1

Это то, что я сделал:

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

viewTitle вид создан в XIb, он принимает размер Панель навигации и после того, как он был добавлен titleView настроить размер, чтобы оставить место в Кнопка назад. Похоже, что вращение работает нормально.

Смежные вопросы