IOS: панель навигации titleView не изменяет размер правильно, когда телефон вращается
Я пишу приложение для iPhone, которое (как и большинство приложений) поддерживает автоматическое вращение: вы поворачиваете свой телефон, и его виды вращаются и изменяются соответствующим образом.
Но я назначаю пользовательское представление navigationItem.titleView
(область заголовка панели навигации), и я не могу получить это представление для правильного изменения размера, когда телефон вращается.
Я знаю, что вы думаете: "Просто установите его autoresizingMask
на UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight
", но это не так просто. Конечно, если я не установлю свой взгляд autoresizingMask
, то мое представление не изменится; и я хочу, чтобы он изменялся.
Проблема заключается в том, что если я устанавливаю его autoresizingMask
, то он правильно изменяется, пока это вид будет видимым; но размер titleView
становится испорченным в этом сценарии:
- Запустите приложение, удерживая телефон в портретном режиме. Все выглядит хорошо.
- Сделайте что-то, что заставляет приложение нажимать другое представление на стек навигации. Например. щелкните строку таблицы или кнопку, вызывающую вызов
[self.navigationController pushViewController:someOtherViewController animated:YES]
.
- Во время просмотра дочернего контроллера поверните телефон в положение "пейзаж".
- Нажмите кнопку "Назад", чтобы вернуться к просмотру верхнего уровня. На данный момент вид заголовка перепутался: хотя вы держите телефон в альбомном режиме, название заголовка по-прежнему имеет размер, как если бы вы держали его в портретном режиме.
- Наконец, поверните телефон обратно в портретный режим. Теперь ситуация становится еще хуже: размер заголовка уменьшается (поскольку панель навигации уменьшилась), но поскольку она уже слишком мала, теперь она слишком мала.
Если вы хотите воспроизвести это самостоятельно, выполните следующие действия (это немного работает):
- Создайте приложение, используя мастер Xcode "Навигационное приложение".
- Установите его так, чтобы в представлении таблицы верхнего уровня были строки, которые при нажатии на них нажимают подробный вид на стек навигации.
-
Включите этот код как в контроллер верхнего уровня, так и в контроллер подробного представления:
- (BOOL)shouldAutorotateToInterfaceOrientation:
(UIInterfaceOrientation)interfaceOrientation {
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}
-
Включите этот код только в контроллер верхнего уровня:
- (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;
}
-
Наконец, следуйте моим инструкциям.
Итак... я делаю что-то неправильно? Есть ли способ сделать мой TitleView всегда правильно измененным?
Ответы
Ответ 1
(Отвечая на мой собственный вопрос)
Я получил эту работу, вручную отслеживая поля titleView (его расстояние от краев панели навигации) - сохранение при исчезновении представления и восстановление при появлении вида.
Идея заключается в том, что мы не восстанавливаем titleView до точного размера, который был ранее; скорее, мы восстанавливаем его так, чтобы он имел те же поля, что и раньше. Таким образом, если телефон повернулся, заголовок будет иметь новый, соответствующий размер.
Вот мой код:
В моем представлении файл .h htt:
@interface MyViewController ...
{
CGRect titleSuperviewBounds;
UIEdgeInsets titleViewMargins;
}
На мой взгляд, файл .m файла:
/**
* Helper function: Given a parent view bounds and a child view 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 orientation while we are in a different view, then when we
// return to this view, we can fix the titleView 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 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;
}
}
}
Ответ 2
Вы также должны установить 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];
Ответ 3
У меня было что-то похожее, но оно возвращалось (добавлялось) в контроллер корневого представления. В конечном счете, я пошёл со следующим:
[[self navigationController] setNavigationBarHidden:YES animated:NO];
[[self navigationController] popViewControllerAnimated:YES];
[[self navigationController] setNavigationBarHidden:NO animated:NO];
И это сработало. Возможно, был лучший способ, но - после всех часов, которые я уже потратил на эту проблему, - это было достаточно для меня.
Ответ 4
Я рассмотрел эту же проблему, отслеживая начальный кадр 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];
}
}
Ответ 5
Для IOS5 и далее, поскольку это старый вопрос... Вот как я справился с той же проблемой, что и текст заголовка, не совпадающий правильно.
[[UINavigationBar appearance] setTitleVerticalPositionAdjustment:2 forBarMetrics:UIBarMetricsLandscapePhone];
Протестировано на ios5/6 sims отлично работает.
Ответ 6
Это то, что я сделал:
self.viewTitle.frame = self.navigationController.navigationBar.frame;
self.navigationItem.titleView = self.viewTitle;
ViewTitle - это представление, созданное в xib, оно принимает размер навигационной панели, и после того, как оно было добавлено, titleView отрегулирует размер, чтобы оставить место для кнопки "Назад". Похоже, что вращение работает нормально.
Ответ 7
У меня была такая же проблема, но я, похоже, обходился с последующим кодом.
- (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, но я надеюсь, что это поможет вам.