Programing

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

lottogame 2020. 8. 31. 08:18
반응형

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


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

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

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


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

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

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

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

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.


내가 말할 수있는 한, 때로는 비표준 스타일링을 수행하기 위해 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--지금은 작동합니다.


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

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

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

[navigationController pushViewController:rootViewController animated:YES];

Michael's solution works, but you can avoid NSKeyedArchiver and 'xxd' utility. Simply Subclass UINavigationController and override initWithRootViewController, loading your custom NavigationController NIB directly:

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

Update: Using object_SetClass() no longer works as if iOS5 GM. An alternate solution has been added below.

Use NSKeyedUnarchiver to manually set the unarchive class for the nav bar.

   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"];




Note: This original solution only works pre-iOS5:

There is a great solution, which I posted here -- inject navBar subclass directly into your view the UINavigationController:

#import <objc/runtime.h>

- (void)viewDidLoad {
    [super viewDidLoad];

    object_setClass(self.navigationController.navigationBar, [MyNavBar class]);
    // the rest of your viewDidLoad code
}

One scenario I've found that we need to use subclass rather than category is to set navigationbar backgroundcolor with pattern image, because in iOS5 overwriting drawRect using category does not work any more. If you want to support ios3.1-5.0, the only way you can do is to subclass navigationbar.


These category methods are dangerous and not for novices. Also the complication with iOS4 and iOS5 being different, makes this an area that can cause bugs for many people. Here is a simple subclass that I use that supports iOS4.0 ~ iOS6.0 and is very simply.

.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

It's not recommended to subclass the UINavigationBar class. The preferred way to customizing the navigation bar is to set it's properties to make it appear as you wish and use custom views inside UIBarButtonItems along with a delegate to get the desired behavior.

What are you trying to do that needs subclassing?

Also, I don't think IB is actually replacing the navigation bar. I'm pretty sure it's simply not displaying the default one and has your custom nav bar as a subview. If you call UINavigationController.navigationBar, do you get an instance of your bar?


If you want to subclass navBar just to change background image - there is no need to in iOS 5. There will be method like this one setBackgroundImage


Further to obb64's comment, I ended up using his trick with setViewControllers:animated: to set the controller as the rootController for the navigationController loaded from the nib. Here's the code I'm using:

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

참고URL : https://stackoverflow.com/questions/1869331/set-a-custom-subclass-of-uinavigationbar-in-uinavigationcontroller-programmatica

반응형