Показать UITabBar при нажатии UIViewController - PullRequest
8 голосов
/ 22 февраля 2011

Вот моя ситуация:
У меня есть UINavigationController внутри UITabBarController. Когда я детализирую навигационный контроллер, в какой-то момент мне приходится скрывать UITabBar, потому что я хочу, чтобы представление имело как можно больше места.
Я делаю это с помощью self.hidesBottomBarWhenPushed = YES внутри толкаемого UIViewController, и это работает довольно хорошо.
Тем не менее, я хочу показать UITabBar обратно в следующих выдвинутых контроллерах. Я пытался вставить self.hidesBottomBarWhenPushed = NO в другие контроллеры, но UITabBar не вернется.

Кажется, это нормально, так как в документации говорится:

hidesBottomBarWhenPushed

If YES, the bar at the bottom of the screen is hidden; otherwise, NO. If YES, the bottom bar remains hidden until the view controller is popped from the stack.

И действительно, когда контроллер с этим свойством, установленным в yes, извлекается, панель вкладок возвращается.

Есть ли какой-нибудь правильный способ показать UITabBar, когда контроллер нажат, когда он скрыт?

Заранее спасибо

Ответы [ 3 ]

13 голосов
/ 15 июля 2012

hidesBottomBarWhenPressed не считается устаревшим.Я обнаружил, что очень просто добиться скрытия и показа UITabBar, используя следующий метод:

self.hidesBottomBarWhenPushed = YES;
[self.navigationController pushViewController:detailViewController animated:YES];
[detailViewController release];
self.hidesBottomBarWhenPushed = NO;

Так что сразу после нажатия detailViewConroller вы должны сбросить свойство скрытия обратно в NO.Таким образом, он появится снова, когда появится подробный вид.нет необходимости в каких-либо дополнительных изменениях в viewWillApear / исчезать и т. д. Наслаждайтесь.

6 голосов
/ 22 февраля 2011

Хорошо, у нас очень длинный путь.

При чтении из документа поведение по умолчанию становится понятным: если свойство hides... контроллера представления имеет значение YES, панель вкладок скрывается до тех пор, пока контроллер представления не будет извлечен. То, что вы хотите, прямо противоречит этому, и по разным причинам я бы сначала рекомендовал не использовать этот подход.

Однако это не значит, что это невозможно реализовать.


  • Установка hides... свойства обратно в NO

Вы не можете изменить поведение по умолчанию. Чтобы отобразить панель вкладок, все контроллеры представления в стеке должны установить для своего свойства hides... значение NO. Таким образом, в представлении, где панель вкладок скрыта, если вы хотите снова отобразить панель при нажатии нового представления, вам необходимо снова установить свойство hides... контроллера предыдущего представления в NO.

Непосредственно перед тем, как нажать новый контроллер представления, установите для свойства значение NO.

// ... prepare viewControllerToPush

self.hidesBottomBarWhenPushed = NO;
[self.navigationController pushViewController:viewControllerToPush animated:YES];
[viewControllerToPush release];

Сделав это, вы снова получите панель вкладок. Однако вы узнаете, что панель вкладок вставлена ​​слева, а новый вид - справа. Это явно нежелательно, поэтому нам нужно это исправить.


  • Переопределение действия слоя для панели вкладок

Дело в том, что действие слоя по умолчанию (анимация), используемое при повторном появлении панели вкладок, - это анимация push-перехода слева. UITabBar реализует метод - (id < CAAction >)actionForLayer:(CALayer *)layer forKey:(NSString *)key, который указывает использовать анимацию слева для случая. Нам нужно переопределить этот метод, чтобы вместо этого вернуть анимацию справа.

Чтобы показать панель вкладок назад, Cocoa изменяет свойство position своего слоя. Поэтому наш новый метод должен возвращать анимацию справа для ключа position, а для всех остальных ключей он должен использовать реализацию по умолчанию. Обратите внимание, что использование position для панели вкладок не задокументировано Apple, поэтому может быть изменено без уведомления в следующих версиях. В любом случае, мы реализуем что-то, что прямо противоречит спецификации Apple, поэтому не могу много жаловаться.

Однако вы не можете просто использовать подклассы для переопределения метода. Потому что даже если у вас есть собственный подкласс UITabBar, вы не можете изменить класс UITabBarController, чтобы использовать его вместо UITabBar по умолчанию.

Итак, все становится немного сложнее. Чтобы внедрить свою собственную логику в класс UITabBar, вы должны «поменять» реализацию сообщения actionForLayer: forKey:.

Сначала добавьте новый метод в класс UITabBar, используя категорию.

@interface UITabBar(customAction)
@end

@implementation UITabBar(customAction)

- (id < CAAction >)customActionForLayer:(CALayer *)layer forKey:(NSString *)key {
    if ([key isEqualToString:@"position"]) {
        CATransition *pushFromRight = [[CATransition alloc] init];
        pushFromRight.duration = 0.25; 
        pushFromRight.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn]; 
        pushFromRight.type = kCATransitionPush; 
        pushFromRight.subtype = kCATransitionFromRight;
        return [pushFromRight autorelease];
    } 
return [self defaultActionForLayer:layer forKey:key];
}
@end

А в методе viewDidAppear контроллера панели вкладок введите следующие коды.

Method original = class_getInstanceMethod([UITabBar class], @selector(actionForLayer:forKey:));
Method custom = class_getInstanceMethod([UITabBar class], @selector(customActionForLayer:forKey:));

class_replaceMethod([UITabBar class], @selector(actionForLayer:forKey:), method_getImplementation(custom), method_getTypeEncoding(custom));
class_addMethod([UITabBar class], @selector(defaultActionForLayer:forKey:), method_getImplementation(original), method_getTypeEncoding(original));

Вы хотите сделать это в viewDidAppear, а не viewDidLoad, потому что в противном случае панель вкладок будет сдвигаться вправо при первом появлении приложения.

Теперь, когда экземпляр UITabBar получает сообщение actionsForLayer forKey:, вызывается пользовательский метод customActionForLayer forKey:. Он перехватывает клавишу position и возвращает анимацию справа. Если это для другого ключа, он вызывает исходную реализацию сообщения, которая теперь связана с сообщением defaultActionsForLayer forKey:.


Хорошо, вот и мы. Помните, что при возврате представлений вам может потребоваться установить для свойства hides... значение YES, поскольку вы устанавливаете его в значение NO при нажатии нового представления (и выполняете некоторые аналогичные приемы для его правильной анимации).

Я потратил некоторое время на это, но по иронии судьбы, должен сказать * Не используйте этот снова, потому что он использует недокументированную информацию классов Какао (клавиша «позиция» для анимации панели вкладок) , противоречит задокументированному поведению и противоречит руководству Apple по человеческому интерфейсу. Вы можете обнаружить, что ваше приложение не будет работать одинаково со следующими версиями SDK, что пользователи не смогут легко перенять интерфейс или даже Apple отклонит ваше приложение в магазине приложений.

Тогда зачем мне мой ответ? Ну, я думаю, пример некоторых интересных тем по разработке под iOS (и доказательство того, что я сегодня ужасно непродуктивен: P).

4 голосов
/ 27 июля 2012

Вот мой подход к этому при использовании раскадровок в iOS 5:

Установите свойство hidesBottomBarWhenPushed на NO перед выполнением push-перехода:

- (void)prepareForSegue:(UIStoryboardSegue*)segue sender:(id)sender
{
   if( [segue.identifier isEqualToString:@"PushDetailView"] )
   {
      self.hidesBottomBarWhenPushed = NO;
   }

   [super prepareForSegue:segue sender:sender];
}

Идентификатор переходаочевидно, что вы можете назвать.

Установите его обратно в YES сразу после исчезновения представления контроллера вида:

- (void)viewWillDisappear:(BOOL)animated
{
   self.hidesBottomBarWhenPushed = YES;

   [super viewWillDisappear:animated];
}

Работает как брелок (протестировано на iOS 5.1), используя всеправильные анимации для UITabBar.

...