Кто-нибудь знает, как я могу использовать свой собственный подкласс, UINavigationBar
если я создаю экземпляр UINavigationController
программно (без IB)?
Перетащите UINavigationController
в IB, покажите мне панель навигации и используя Identity Inspectory, я могу изменить тип класса и установить свой собственный подкласс, UINavigationBar
но программно я не могу, navigationBar
свойство контроллера навигации доступно только для чтения ...
Что мне делать, чтобы программно настроить панель навигации? IB более «мощный», чем «код»? Я считал, что все, что можно сделать в IB, можно сделать и программно.
Ответы:
Вам не нужно гадить с XIB, просто используйте KVC.
[self.navigationController setValue:[[[CustomNavBar alloc]init] autorelease] forKeyPath:@"navigationBar"];
источник
Начиная с iOS5, Apple предоставляет способ сделать это напрямую. Справка
UINavigationController *navigationController= [[UINavigationController alloc]initWithNavigationBarClass:[CustomNavBar class] toolbarClass:nil]; [navigationController setViewControllers:[NSArray arrayWithObject:yourRootViewController]];
источник
Начиная с iOS 4, вы можете использовать
UINib
класс для решения этой проблемы.UINavigationBar
подкласс.UINavigationController
качестве единственного объекта.UINavigationController
«сUINavigationBar
до вашего пользовательского подкласса.[navController setViewcontrollers[NSArray arrayWithObject:myRootVC]];
[navController pushViewController:myRootVC];
В коде:
UINib *nib = [UINib nibWithNibName:@"YourCustomXib" bundle:nil]; UINavigationController *navController = [[nib instantiateWithOwner:nil options:nil] objectAtIndex:0];
Теперь у вас есть
UINavigationController
файл customUINavigationBar
.источник
Насколько я могу судить, иногда действительно необходимо создать подкласс UINavigationBar, чтобы провести нестандартный рестайлинг. Иногда можно избежать этого, используя категории , но не всегда.
В настоящее время, насколько мне известно, единственный способ установить пользовательский UINavigationBar в UIViewController - это через IB (то есть через архив) - вероятно, этого не должно быть, но пока мы должны жить с этим.
Часто это нормально, но иногда использование IB нецелесообразно.
Итак, я увидел три варианта:
Вариант 1 был для меня в данном случае невозможным (или, по крайней мере, слишком раздражающим), поскольку мне нужно было создать UINavigationController программно, 2 - немного опасный и, на мой взгляд, вариант в крайнем случае, поэтому я выбрал вариант 3.
Мой подход состоял в том, чтобы создать архив «шаблонов» для UINavigationController и разархивировать его, вернув его
initWithRootViewController
.Вот как:
В IB я создал UINavigationController с соответствующим набором классов для UINavigationBar.
Затем я взял существующий контроллер и сохранил его архивную копию, используя
+[NSKeyedArchiver archiveRootObject:toFile:]
. Я только что сделал это в делегате приложения в симуляторе.Затем я использовал утилиту xxd с флагом -i, чтобы сгенерировать код c из сохраненного файла, чтобы встроить заархивированную версию в свой подкласс (
xxd -i path/to/file
).Внутри
initWithRootViewController
я разархивирую этот шаблон и устанавливаю для себя результат разархивирования:// This is the data from [NSKeyedArchiver archivedDataWithRootObject:controller], where // controller is a CTNavigationController with navigation bar class set to CTNavigationBar, // from IB. This c code was created using 'xxd -i' static unsigned char archived_controller[] = { 0x62, 0x70, 0x6c, 0x69, 0x73, 0x74, 0x30, 0x30, 0xd4, 0x01, 0x02, 0x03, ... }; static unsigned int archived_controller_len = 682; ... - (id)initWithRootViewController:(UIViewController *)rootViewController { // Replace with unarchived view controller, necessary for the custom navigation bar [self release]; self = (CTNavigationController*)[NSKeyedUnarchiver unarchiveObjectWithData:[NSData dataWithBytes:archived_controller length:archived_controller_len]]; [self setViewControllers:[NSArray arrayWithObject:rootViewController]]; return [self retain]; }
Затем я могу просто взять новый экземпляр моего подкласса UIViewController, в котором установлена настраиваемая панель навигации:
UIViewController *modalViewController = [[[CTNavigationController alloc] initWithRootViewController:myTableViewController] autorelease]; [self.navigationController presentModalViewController:modalViewController animated:YES];
Это дает мне модальный UITableViewController с настроенной панелью навигации и панелью инструментов, а также с настраиваемым классом панели навигации. Мне не нужно было делать какую-либо слегка неприятную замену методов, и мне не нужно возиться с перьями, когда я действительно просто хочу работать программно.
Я хотел бы увидеть эквивалент
+layerClass
UINavigationController+navigationBarClass
- но пока это работает.источник
Я использую "вариант 1"
Создайте nib-файл, содержащий только UINavigationController. И установите для класса UINavigationBar мой собственный класс.
self.navigationController = [[[NSBundle mainBundle] loadNibNamed:@"navigationbar" owner:self options:nil] lastObject]; [navigationController pushViewController:rootViewController animated:YES];
источник
Решение Майкла работает, но вы можете избежать использования NSKeyedArchiver и утилиты xxd. Просто создайте подкласс UINavigationController и переопределите
initWithRootViewController
, загрузив свой собственный NIB NavigationController напрямую:- (id) initWithRootViewController:(UIViewController *)rootViewController { [self release]; self = [[[[NSBundle mainBundle] loadNibNamed:@"CTNavigationController" owner:nil options:nil] objectAtIndex:0] retain]; [self setViewControllers:[NSArray arrayWithObject:rootViewController]]; return self; }
источник
Обновление: использование
object_SetClass()
больше не работает, как если бы iOS5 GM. Ниже добавлено альтернативное решение.Используйте NSKeyedUnarchiver, чтобы вручную установить класс разархивирования для панели навигации.
MyViewController *controller = [[[MyViewController alloc] init] autorelease]; NSKeyedUnarchiver *unarchiver = [[[NSKeyedUnarchiver alloc] initForReadingWithData:[NSKeyedArchiver archivedDataWithRootObject:controller]] autorelease]; [unarchiver setClass:[MyNavigationBar class] forClassName:@"UINavigationBar"]; controller = [unarchiver decodeObjectForKey:@"root"];
Примечание: это оригинальное решение работает только до iOS5:
Есть отличное решение, которое я разместил здесь - вставьте подкласс navBar прямо в ваше представление
UINavigationController
:#import <objc/runtime.h> - (void)viewDidLoad { [super viewDidLoad]; object_setClass(self.navigationController.navigationBar, [MyNavBar class]); // the rest of your viewDidLoad code }
источник
Я обнаружил, что в одном из сценариев нам нужно использовать подкласс, а не категорию, это установить цвет фона панели навигации с изображением шаблона, потому что в iOS5 перезапись drawRect с использованием категории больше не работает. Если вы хотите поддерживать ios3.1-5.0, единственный способ сделать это - создать подкласс панели навигации.
источник
Эти категориальные методы опасны и не для новичков. Кроме того, сложность, связанная с различием iOS4 и iOS5, делает эту область причиной ошибок для многих людей. Вот простой подкласс, который я использую, который поддерживает iOS4.0 ~ iOS6.0 и очень прост.
.час
@interface XXXNavigatioNBar : UINavigationBar @end
.m
#import "XXXNavigationBar.h" #import <objc/runtime.h> @implementation XXXNavigationBar - (void) didMoveToSuperview { if( [self respondsToSelector: @selector(setBackgroundImage:forBarMetrics:)]) { //iOS5.0 and above has a system defined method -> use it [self setBackgroundImage: [UIImage imageNamed: @"nav-bar"] forBarMetrics: UIBarMetricsDefault]; } else { //iOS4.0 requires us to override drawRect:. BUT!! //If you override drawRect: on iOS5.0 the system default will break, //so we dynamically add this method if required IMP implementation = class_getMethodImplementation([self class], @selector(iOS4drawRect:)); class_addMethod([self class], @selector(drawRect:), implementation, "v@:{name=CGRect}"); } } - (void)iOS4drawRect: (CGRect) rect { UIImage* bg = [UIImage imageNamed:@"nav-bar-blue"]; [bg drawInRect: rect]; } @end
источник
Это не рекомендуется , чтобы подкласс
UINavigationBar
класса. Предпочтительный способ настройки панели навигации - установить ее свойства, чтобы она отображалась так, как вы хотите, и использовать настраиваемые представления внутри UIBarButtonItems вместе с делегатом для получения желаемого поведения.Что вы пытаетесь сделать, чтобы создать подклассы?
Кроме того, я не думаю, что IB фактически заменяет панель навигации. Я почти уверен, что он просто не отображает панель по умолчанию и имеет вашу настраиваемую панель навигации в качестве подпредставления. Если вы вызовете UINavigationController.navigationBar, получите ли вы экземпляр своей панели?
источник
Если вы хотите создать подкласс navBar только для изменения фонового изображения - в iOS 5 в этом нет необходимости. Будет такой метод setBackgroundImage
источник
setBackgroundImage:forBarMetrics:
как описано здесь: developer.apple.com/library/IOS/#documentation/UIKit/Reference/…В дополнение к комментарию obb64, я закончил тем, что использовал его трюк,
setViewControllers:animated:
чтобы установить контроллер в качестве контроллераrootController
дляnavigationController
загружаемого из пера. Вот код, который я использую:- (void) presentModalViewControllerForClass: (Class) a_class { UINavigationController *navController = [[[NSBundle mainBundle] loadNibNamed: @"CustomNavBar" owner:self options:nil] lastObject]; LoginSignupBaseViewController *controller = [[a_class alloc] initWithNibName: nil bundle: nil]; controller.navigationController = navController; [navController setViewControllers: A(controller) animated: NO]; [self presentModalViewController: navController animated: YES]; [controller release]; }
источник