Как программно подклассифицировать UINavigationBar для UINavigationController?

Я использую пользовательскую функцию drawRect для рисования на UINavigationBar в моем приложении в iOS4, она не использует изображения, а только CoreGraphics.

Поскольку вы не можете реализовать drawRect в категории UINavigationBar в iOS5, Apple предлагает подкласс UINavigationBar.

Как можно заменить UINavigationBar моим подклассом на UINavigationController (поэтому он будет совместим с iOS4 и iOS5), если свойство navigationBar доступно только для чтения?

@property(nonatomic, readonly) UINavigationBar *navigationBar

Я вообще не использую XIB в своем приложении, поэтому добавление UINavigationBar в NIB и изменение класса через InterfaceBuilder не является вариантом.

Ответ 1

Как и в iOS6, теперь это довольно просто сделать без swizzling или messing с другими классами с помощью метода UINavigationControllers initWithNavigationBarClass:toolbarClass:

- (id)initWithNavigationBarClass:(Class)navigationBarClass 
                    toolbarClass:(Class)toolbarClass;

Из документов:

Инициализирует и возвращает вновь созданный контроллер навигации, который использует ваши пользовательские подклассы.

Ответ обновлен для iOS6.

Ответ 2

В iOS 6 они добавили новый метод к UINavigationController, который также доступен в iOS 5:

- (id)initWithNavigationBarClass:(Class)navigationBarClass
                    toolbarClass:(Class)toolbarClass;

Теперь вы можете просто передать свой собственный класс при создании экземпляра контроллера навигации.

Ответ 3

Единственный поддерживаемый способ сделать это в iOS 4 - это использовать метод построителя интерфейса. Вам не нужно использовать IB, чтобы делать что-либо, кроме набора подкласса UINavigationBar (вы все равно можете сделать все свое представление настроенным программным путем).

Ответ 4

- (id)initWithNavigationBarClass:(Class)navigationBarClass toolbarClass:(Class)toolbarClass;

Я столкнулся с одной проблемой с указанным выше методом. Для инициализации UINavigationController существует метод initWithRootViewController. Но, если я использую "initWithNavigationBarClass" для запуска UINavigationController, тогда я не могу установить "rootViewController" для UINavigationController.

Эта ссылка Изменение UINavigationController rootViewController помогло мне добавить rootViewController после инициализации UINavigationController с помощью "initWithNavigationBarClass". В принципе, трюк заключается в подклассе UINavigationController. Хотя я еще не тестировал его в IB, но он отлично работает в коде.

Ответ 5

Были проблемы с ответами 2-4 в iOS 4 (из ответа AnswerBot) и нужен способ загрузки программного обеспечения UINavigationController (хотя метод NIB работал)... Итак, я сделал это:

Создан пустой файл XIB (не устанавливайте владельца файла), добавьте UINavigationController (укажите его собственный класс UINavigationController), измените UINavigationBar на свой собственный класс (здесь он CustomNavigationBar), затем создайте следующий пользовательский заголовок класса ( CustomNavigationController.h в этом случае):

#import <UIKit/UIKit.h>

@interface CustomNavigationController : UINavigationController
+ (CustomNavigationController *)navigationController;
+ (CustomNavigationController *)navigationControllerWithRootViewController:(UIViewController *)rootViewController;
@end

и пользовательская реализация (CustomNavigationController.mm)

#import "CustomNavigationController.h"

@interface CustomNavigationController ()

@end

@implementation CustomNavigationController
+ (CustomNavigationController *)navigationController
{
    NSArray *nib = [[NSBundle mainBundle] loadNibNamed:@"CustomNavigationController" owner:self options:nil];
    CustomNavigationController *controller = (CustomNavigationController *)[nib objectAtIndex:0];
    return controller;
}


+ (CustomNavigationController *)navigationControllerWithRootViewController:(UIViewController *)rootViewController
{
    CustomNavigationController *controller = [CustomNavigationController navigationController];
    [controller setViewControllers:[NSArray arrayWithObject:rootViewController]];
    return controller;
}

- (id)init
{
    self = [super init];
    [self autorelease]; // We are ditching the one they allocated.  Need to load from NIB.
    return [[CustomNavigationController navigationController] retain]; // Over-retain, this should be alloced
}

- (id)initWithRootViewController:(UIViewController *)rootViewController
{
    self = [super init];
    [self autorelease];
    return [[CustomNavigationController navigationControllerWithRootViewController:rootViewController] retain];
}
@end

Затем вы можете просто запустить этот класс вместо UINavigationController, и у вас будет настраиваемая панель навигации. Если вы хотите сделать это в xib, измените класс UINavigationController и UINavigationBar внутри вашего XIB.

Ответ 6

Вид поправки к приведенным выше ответам для тех, кто все еще хочет использовать initWithRootViewController. Подкласс UINavigationController, а затем:

- (id) initWithRootViewController:(UIViewController *)rootViewController
{
    self = [super initWithNavigationBarClass:[CustomNavigationBar class] toolbarClass:nil];
    if (self)
    {
        self.viewControllers = [NSArray arrayWithObjects:rootViewController, nil];
    }

    return self;
}