Czy ktoś wie, jak mogę użyć mojej niestandardowej podklasy, UINavigationBar
jeśli utworzę wystąpienie UINavigationController
programowo (bez IB)?
Przeciągnij UINavigationController
w IB pokaż mi pod Paskiem Nawigacyjnym i używając Inspekcji tożsamości Mogę zmienić typ klasy i ustawić własną podklasę, UINavigationBar
ale programowo nie mogę, navigationBar
właściwość kontrolera nawigacji jest tylko do odczytu ...
Co mam zrobić, aby programowo dostosować pasek nawigacji? Czy IB jest bardziej „potężny” niż „kod”? Wierzyłem, że wszystko, co można zrobić w IB, można również zrobić programowo.
Odpowiedzi:
Nie musisz grzebać w XIB, po prostu użyj KVC.
[self.navigationController setValue:[[[CustomNavBar alloc]init] autorelease] forKeyPath:@"navigationBar"];
źródło
Od iOS5 firma Apple udostępnia metodę bezpośredniej obsługi. Odniesienie
UINavigationController *navigationController= [[UINavigationController alloc]initWithNavigationBarClass:[CustomNavBar class] toolbarClass:nil]; [navigationController setViewControllers:[NSArray arrayWithObject:yourRootViewController]];
źródło
Od wersji iOS 4 możesz użyć
UINib
klasy, aby rozwiązać ten problem.UINavigationBar
podklasę.UINavigationController
jako pojedynczy obiekt.UINavigationController
„sUINavigationBar
do swojej niestandardowej podklasy.[navController setViewcontrollers[NSArray arrayWithObject:myRootVC]];
[navController pushViewController:myRootVC];
W kodzie:
UINib *nib = [UINib nibWithNibName:@"YourCustomXib" bundle:nil]; UINavigationController *navController = [[nib instantiateWithOwner:nil options:nil] objectAtIndex:0];
Teraz masz
UINavigationController
ze swoim niestandardowymUINavigationBar
.źródło
O ile wiem, czasami rzeczywiście konieczne jest utworzenie podklasy UINavigationBar w celu wykonania niestandardowej zmiany stylizacji. Czasami można tego uniknąć, używając kategorii , ale nie zawsze.
Obecnie, o ile wiem, jedynym sposobem ustawienia niestandardowego UINavigationBar w kontrolerze UIViewController jest IB (czyli archiwum) - prawdopodobnie nie powinno tak być, ale na razie musimy z tym żyć.
Często jest to w porządku, ale czasami użycie IB nie jest naprawdę wykonalne.
Widziałem więc trzy opcje:
Opcja 1 była dla mnie niewykonalna (lub przynajmniej zbyt irytująca) w tym przypadku, ponieważ musiałem programowo utworzyć UINavigationController, 2 jest trochę niebezpieczna i moim zdaniem jest bardziej ostateczną opcją, więc wybrałem opcję 3.
Moje podejście polegało na utworzeniu archiwum „szablonu” kontrolera UINavigationController i przywróceniu go z archiwum, zwracając go w formacie
initWithRootViewController
.Oto jak:
W IB stworzyłem UINavigationController z odpowiednim zestawem klas dla UINavigationBar.
Następnie wziąłem istniejący kontroler i zapisałem jego zarchiwizowaną kopię za pomocą
+[NSKeyedArchiver archiveRootObject:toFile:]
. Właśnie to zrobiłem w ramach delegata aplikacji, w symulatorze.Następnie użyłem narzędzia „xxd” z flagą -i, aby wygenerować kod c z zapisanego pliku, aby osadzić zarchiwizowaną wersję w mojej podklasie (
xxd -i path/to/file
).Wewnątrz
initWithRootViewController
rozpakowuję ten szablon i ustawiam się na wynik przywrócenia z archiwum:// 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]; }
Następnie mogę po prostu pobrać nowe wystąpienie mojej podklasy UIViewController, która ma ustawiony niestandardowy pasek nawigacji:
UIViewController *modalViewController = [[[CTNavigationController alloc] initWithRootViewController:myTableViewController] autorelease]; [self.navigationController presentModalViewController:modalViewController animated:YES];
Daje mi to modalny UITableViewController ze skonfigurowanym paskiem nawigacji i paskiem narzędzi oraz z niestandardową klasą paska nawigacji. Nie musiałem wykonywać żadnej nieco nieprzyjemnej zamiany metody i nie muszę majstrować przy końcówkach, gdy naprawdę chcę pracować programowo.
Chciałbym zobaczyć odpowiednik
+layerClass
w UINavigationController -+navigationBarClass
- ale na razie to działa.źródło
Używam „opcji 1”
Utwórz plik nib zawierający tylko element UINavigationController. I ustaw klasę UINavigationBar na moją klasę niestandardową.
self.navigationController = [[[NSBundle mainBundle] loadNibNamed:@"navigationbar" owner:self options:nil] lastObject]; [navigationController pushViewController:rootViewController animated:YES];
źródło
Rozwiązanie Michaela działa, ale możesz uniknąć NSKeyedArchiver i narzędzia „xxd”. Po prostu podklasę UINavigationController i zastąpienie
initWithRootViewController
, ładując bezpośrednio niestandardowy plik 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; }
źródło
Aktualizacja: Używanie
object_SetClass()
nie działa już tak, jakby iOS5 GM. Alternatywne rozwiązanie zostało dodane poniżej.Użyj NSKeyedUnarchiver, aby ręcznie ustawić klasę przywracania z archiwum dla paska nawigacji.
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"];
Uwaga: to oryginalne rozwiązanie działa tylko przed iOS5:
Jest świetne rozwiązanie, które zamieściłem tutaj - wstrzyknij podklasę navBar bezpośrednio do swojego widoku
UINavigationController
:#import <objc/runtime.h> - (void)viewDidLoad { [super viewDidLoad]; object_setClass(self.navigationController.navigationBar, [MyNavBar class]); // the rest of your viewDidLoad code }
źródło
Jednym ze scenariuszy, które odkryłem, że musimy użyć podklasy zamiast kategorii, jest ustawienie koloru tła paska nawigacji z obrazem wzoru, ponieważ w iOS5 nadpisywanie drawRect za pomocą kategorii już nie działa. Jeśli chcesz obsługiwać ios3.1-5.0, jedynym sposobem, w jaki możesz to zrobić, jest podklasa paska nawigacji.
źródło
Te metody kategorii są niebezpieczne i nie są dla nowicjuszy. Również komplikacje związane z różnymi systemami iOS4 i iOS5 sprawiają, że jest to obszar, który może powodować błędy dla wielu osób. Oto prosta podklasa, której używam, która obsługuje iOS4.0 ~ iOS6.0 i jest bardzo prosta.
.h
@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
źródło
Nie zaleca się tworzenia podklasy tej
UINavigationBar
klasy. Preferowanym sposobem dostosowywania paska nawigacji jest ustawienie jego właściwości tak, aby wyglądał tak, jak chcesz, i używanie niestandardowych widoków w UIBarButtonItems wraz z delegatem w celu uzyskania żądanego zachowania.Co próbujesz zrobić, co wymaga podklasy?
Nie sądzę też, że IB faktycznie zastępuje pasek nawigacyjny. Jestem prawie pewien, że po prostu nie wyświetla domyślnego i ma niestandardowy pasek nawigacyjny jako widok podrzędny. Jeśli wywołasz UINavigationController.navigationBar, czy otrzymasz wystąpienie swojego paska?
źródło
Jeśli chcesz podklasę navBar tylko po to, aby zmienić obraz tła - nie ma takiej potrzeby w iOS 5. Będzie metoda taka jak ta setBackgroundImage
źródło
setBackgroundImage:forBarMetrics:
zgodnie z opisem tutaj: developer.apple.com/library/IOS/#documentation/UIKit/Reference/…W nawiązaniu do obb64 w komentarzu, skończyło się używając swojego triku z
setViewControllers:animated:
ustaw jako kontrolerrootController
dlanavigationController
ładowane z nib. Oto kod, którego używam:- (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]; }
źródło