프로그래밍 방식으로 UINavigationController에서 UINavigationBar의 사용자 지정 하위 클래스 설정


92

IB없이 프로그래밍 방식으로 UINavigationBar인스턴스화 하는 경우 사용자 지정 하위 클래스를 사용할 수있는 방법을 아는 사람이 UINavigationController있습니까?

UINavigationControllerIB에서 드래그하면 내비게이션 바 아래에 표시되고 Identity Inspectory를 사용하여 클래스 유형을 변경하고 자신의 하위 클래스를 설정할 UINavigationBar수 있지만 프로그래밍 방식으로는 할 수 없습니다. navigationBar내비게이션 컨트롤러의 속성은 읽기 전용입니다.

프로그래밍 방식으로 탐색 모음을 사용자 지정하려면 어떻게해야합니까? IB가 "코드"보다 "강력"합니까? IB에서 할 수있는 모든 것은 프로그래밍 방식으로도 할 수 있다고 믿었습니다.


다른 곳에서 해결책을 찾았습니까?
prendio2

그것에 대해 어떤 대답을 얻었습니까?
Hrushikesh Betai 2012 년

답변:


89

XIB를 다룰 필요는 없습니다. 단순히 KVC를 사용하면됩니다.

[self.navigationController setValue:[[[CustomNavBar alloc]init] autorelease] forKeyPath:@"navigationBar"];

이 솔루션은 매력처럼 작동합니다 (적어도 iOS 5.1에서는). 다른 모든 솔루션은 훨씬 더 많은 작업처럼 보입니다. 여전히 단점을 찾고 있습니다.
다니엘

6
탐색 컨트롤러의 @ "navigationBar"키 경로를 어떻게 찾았습니까? 당신이 공유 할 수 있습니다
이크발 칸에게

이로 인해 앱이 거부되지 않을까요? 나는 이것이 실제로 어디에도 문서화 된 것을 보지 못했습니다.
Bani Uppal

감사! iOS 6 베타 3에서도 잘 작동합니다! @BaniUppal KVC는 확실히 문서화되어 있으며, 찾기가 약간 어려운 키와 함께 사용하고 있습니다. 그것이 요점이라고 생각합니다.
Johannes Lund

9
이것은
엉뚱한

66

iOS5부터 애플은이를 직접 수행하는 방법을 제공합니다. 참고

UINavigationController *navigationController= [[UINavigationController alloc]initWithNavigationBarClass:[CustomNavBar class] toolbarClass:nil];
[navigationController setViewControllers:[NSArray arrayWithObject:yourRootViewController]];

@nonamelive 실제로는 아닙니다. iOS6에 추가됨 : developer.apple.com/library/ios/#releasenotes/General/…
Pascalius

7
예, iOS 6에 추가되었지만 iOS 5에서도 지원됩니다. Apple 엔지니어가 WWDC 2012 세션 216에서 언급했습니다.
nonamelive

37

iOS 4부터 UINib클래스를 사용 하여이 문제를 해결할 수 있습니다 .

  1. 사용자 지정 UINavigationBar하위 클래스를 만듭니다 .
  2. 빈 xib를 만들고 a UINavigationController를 단일 개체로 추가 합니다.
  3. 의 클래스를 설정 UINavigationControllerUINavigationBar사용자 정의 서브 클래스.
  4. 다음 방법 중 하나를 통해 루트 뷰 컨트롤러를 설정합니다.
    • [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사용자 정의 UINavigationBar.


그렇다면 rootViewController를 어떻게 설정합니까?
memmons

[navcontroller setViewControllers : [NSArray arrayWithObject : <YOUR_ROOT_CONTROLLER>]]를 사용합니다
coneybeare

죄송합니다. [navcontroller pushViewController : <YOUR_ROOT_CONTROLLER> animated : NO]를 사용하고 있습니다.
coneybeare

3
IMHO 이것은 때때로 피할 수없는 해킹을 만드는 최소한의 "해킹"방법입니다.
David Pisoni 2011

2
사실, 이상한 문제가 하나 있습니다. 이 작업을 수행하면 새 navigationController의 navigationItem이 (빈) 스택에 푸시하는 viewController에 할당 된 navigationItem 속성으로 설정되지 않습니다.
David Pisoni 2011

26

내가 말할 수있는 한, 때로는 비표준 스타일링을 수행하기 위해 UINavigationBar를 하위 클래스 화하는 것이 실제로 필요합니다. 카테고리 를 사용하여 그렇게하지 않아도되는 경우도 있지만 항상 그런 것은 아닙니다.

현재 내가 아는 한 UIViewController 내에서 사용자 지정 UINavigationBar를 설정하는 유일한 방법은 IB (즉, 아카이브를 통해)를 통하는 것입니다.

이것은 종종 괜찮지 만 때로는 IB를 사용하는 것이 실제로 불가능합니다.

그래서 세 가지 옵션을 보았습니다.

  1. UINavigationBar를 하위 클래스로 만들고 모든 것을 IB에 연결 한 다음 UINavigationController를 원할 때마다 펜촉을로드하는 것에 대해 고민합니다.
  2. 사용 방법 교체 범주 내에서 오히려 서브 클래스보다 UINavigationBar의 동작을 변경하거나하는
  3. UINavigationBar를 하위 클래스로 만들고 UINavigationController의 보관 / 보관 해제에 대해 약간의 작업을 수행합니다.

이 경우 옵션 1은 실행 불가능 (또는 적어도 너무 짜증이 난다)이었습니다. 프로그래밍 방식으로 UINavigationController를 만들어야했기 때문에 2는 약간 위험하고 제 생각에는 최후의 수단 옵션에 가깝기 때문에 옵션 3을 선택했습니다.

내 접근 방식은 UINavigationController의 '템플릿'아카이브를 만들고 아카이브를 해제하여 initWithRootViewController.

방법은 다음과 같습니다.

IB에서는 UINavigationBar에 대한 적절한 클래스 집합을 사용하여 UINavigationController를 만들었습니다.

그런 다음 기존 컨트롤러를 가져 와서 +[NSKeyedArchiver archiveRootObject:toFile:]. 시뮬레이터의 앱 델리게이트 내에서이 작업을 수행했습니다.

그런 다음 -i 플래그와 함께 'xxd'유틸리티를 사용하여 저장된 파일에서 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가 제공됩니다. 약간 불쾌한 메서드 교체를 할 필요가 없었고, 프로그래밍 방식으로 작업하고 싶을 때 펜촉을 만질 필요가 없습니다.

나는 +layerClassUINavigationController 내 에서 동등한 것을보고 싶습니다 +navigationBarClass--지금은 작동합니다.


5

"옵션 1"을 사용합니다.

UINavigationController 만있는 nib 파일을 생성합니다. 그리고 UINavigationBar 클래스를 내 사용자 지정 클래스로 설정합니다.

self.navigationController = [[[NSBundle mainBundle] loadNibNamed:@"navigationbar" owner:self options:nil] lastObject];

[navigationController pushViewController:rootViewController animated:YES];

그래서 nib-file의 적절한 이름은 loadNibNamed : @ "navigationController가 될 것입니다. 당신은 실제로 bar뿐만 아니라 nib에서 전체 navigationController를 얻고 있습니다. 맞습니까?
aneuryzm

이것은 나를 위해 작동하지만 내 tableview에서 옵션을 클릭하면 뒤로 버튼이 사라집니다.
jfisk

5

Michael의 솔루션은 작동하지만 NSKeyedArchiver 및 'xxd'유틸리티를 피할 수 있습니다. UINavigationController를 하위 클래스로 만들고 재정 의하여 initWithRootViewController사용자 지정 NavigationController NIB를 직접로드합니다.

- (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;
}

4

업데이트 : 사용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의 골드 마스터가 이것을 깨뜨 렸습니다. 하지만 다른 옵션을 사용할 수 있으므로 다른 방법으로 편집하겠습니다.
memmons

1

범주 대신 하위 클래스를 사용해야한다는 한 가지 시나리오는 iOS5에서 범주를 사용하여 drawRect를 덮어 쓰는 것이 더 이상 작동하지 않기 때문에 탐색 모음 배경색을 패턴 이미지로 설정하는 것입니다. ios3.1-5.0을 지원하려는 경우 할 수있는 유일한 방법은 탐색 모음을 하위 클래스로 만드는 것입니다.


1

이러한 범주 방법은 위험하며 초보자에게는 적합하지 않습니다. 또한 iOS4와 iOS5의 복잡성이 다르기 때문에 많은 사람들에게 버그를 유발할 수있는 영역입니다. 다음은 iOS4.0 ~ iOS6.0을 지원하고 매우 간단하게 사용하는 간단한 하위 클래스입니다.

.h

@interface XXXNavigatioNBar : UINavigationBar
@end

.미디엄

#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

0

있어 권장되지 서브 클래스 UINavigationBar클래스를. 탐색 모음을 사용자 지정하는 데 선호되는 방법은 원하는대로 표시되도록 속성을 설정하고 UIBarButtonItems 내에서 사용자 지정보기를 델리게이트와 함께 사용하여 원하는 동작을 얻는 것입니다.

서브 클래 싱이 필요한 작업을하려고합니까?

또한 IB가 실제로 내비게이션 바를 대체하고 있다고 생각하지 않습니다. 나는 그것이 단순히 기본을 표시하지 않고 사용자 정의 탐색 모음을 하위보기로 가지고 있다고 확신합니다. UINavigationController.navigationBar를 호출하면 바의 인스턴스를 얻습니까?


2
안녕 벤, 고마워. UINavigationBar를 하위 클래스로 만드는 더 좋은 방법은 아니지만 drawRect : 메서드를 재정의하는 배경 이미지를 설정하고 싶습니다. 문제는 IB를 사용하여 UINavigationController에서 탐색 모음의 클래스를 변경할 수 있지만 프로그래밍 방식으로는 변경할 수 없다는 것입니다. 그리고 예, IB는 실제로 탐색 모음을 대체하고 있습니다. NSLog (@ "% @", self.navigationController.navigationBar); <CustomNavigationBar : 0x1806160; baseClass = UINavigationBar; 프레임 = (0 20; 320 44); clipsToBounds = 예; 불투명 = NO; 자동 크기 조정 = W; layer = <CALayer : 0x1806da0 >>
Duccio

나는 또한이 동일한 기술을 사용하고 있으며, UINavigationBar 카테고리 기술과 달리 iOS5에서도 계속 작동합니다.
David Pisoni


0

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];
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.