UINavigationBar
をプログラムで(IBなしで)インスタンス化する場合、UINavigationController
のカスタムサブクラスを使用する方法を知っている人はいますか?
IBのUINavigationController
をドラッグしてナビゲーションバーの下に表示し、ID検査を使用してクラスタイプを変更し、UINavigationBar
の独自のサブクラスを設定できますが、プログラムではnavigationBar
Navigation Controllerのプロパティは読み取り専用です...
ナビゲーションバーをプログラムでカスタマイズするにはどうすればよいですか? IBは「コード」よりも「強力」ですか? IBでできることはすべて、プログラムによってもできると信じていました。
KIBを使用するだけでXIBをいじる必要はありません。
[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
でUINavigationBar
が得られました。
私の知る限り、UINavigationBarをサブクラス化して、非標準のスタイル変更を行う必要がある場合があります。 categories を使用することで、そうする必要を回避できる場合がありますが、常にではありません。
現在、私が知る限り、UIViewController内でカスタムUINavigationBarを設定するonly方法は、IB経由(つまり、アーカイブ経由)です。おそらくそのようにするべきではありませんが、今のところ、私たちはそれで生きなければなりません。
多くの場合これで問題ありませんが、IBを使用するのは現実的ではありません。
だから、私は3つのオプションを見ました:
プログラム1でUINavigationControllerを作成する必要があるため、この場合、オプション1は実行不可能(または少なくとも面倒)でした。2は少し危険で、私の意見では最後のリゾートオプションなので、オプション3を選択しました。
私のアプローチは、UINavigationControllerの「テンプレート」アーカイブを作成し、それをアーカイブ解除してinitWithRootViewController
で返すことでした。
方法は次のとおりです。
IBでは、UINavigationBarに適切なクラスを設定してUINavigationControllerを作成しました。
次に、既存のコントローラーを取得し、+[NSKeyedArchiver archiveRootObject:toFile:]
を使用してそのアーカイブコピーを保存しました。これは、アプリデリゲート内、シミュレーター内で行いました。
次に、「xxd」ユーティリティを-iフラグとともに使用して、保存したファイルからcコードを生成し、アーカイブバージョンをサブクラス(xxd -i path/to/file
)に埋め込みました。
initWithRootViewController
内で、そのテンプレートをアーカイブ解除し、unarchiveの結果にselfを設定します。
// 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が提供されます。少し厄介なメソッドの置換を行う必要はありませんでしたし、プログラムで作業したいだけなら、ペン先をいじくる必要はありません。
UINavigationController内の+layerClass
に相当するもの-+navigationBarClass
-を見たいのですが、今のところ、これは動作します。
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;
}
「オプション1」を使用します
UINavigationControllerのみを含むnibファイルを作成します。 UINavigationBarクラスをカスタムクラスに設定します。
self.navigationController = [[[NSBundle mainBundle] loadNibNamed:@"navigationbar" owner:self options:nil] lastObject];
[navigationController pushViewController:rootViewController animated:YES];
更新: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より前のバージョンでのみ動作します:
私が投稿した素晴らしい解決策があります here -navBarサブクラスをビューに直接挿入しますUINavigationController
:
#import <objc/runtime.h>
- (void)viewDidLoad {
[super viewDidLoad];
object_setClass(self.navigationController.navigationBar, [MyNavBar class]);
// the rest of your viewDidLoad code
}
これらのカテゴリメソッドは危険であり、初心者向けではありません。また、iOS4とiOS5の複雑さが異なるため、これは多くの人々にバグを引き起こす可能性のある領域になります。 iOS4.0〜iOS6.0をサポートする非常に単純な、私が使用する単純なサブクラスを次に示します。
。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
IOS5ではcategoryを使用してdrawRectを上書きすることはもはや機能しないため、カテゴリではなくサブクラスを使用する必要があることがわかった1つのシナリオは、パターン画像でナビゲーションバーの背景色を設定することです。 ios3.1-5.0をサポートする場合、できる唯一の方法は、ナビゲーションバーをサブクラス化することです。
UINavigationBar
クラスをサブクラス化するのは非推奨です。ナビゲーションバーをカスタマイズする好ましい方法は、プロパティを設定して希望どおりに表示し、UIBarButtonItems内のカスタムビューをデリゲートとともに使用して、目的の動作を取得することです。
サブクラス化が必要な何をしようとしていますか?
また、IBが実際にナビゲーションバーを置き換えているとは思いません。私はそれがデフォルトのものを表示していないだけで、サブビューとしてカスタムナビゲーションバーを持っていると確信しています。 UINavigationController.navigationBarを呼び出すと、バーのインスタンスを取得できますか?
背景画像を変更するためだけにnavBarをサブクラス化する場合-iOS 5では必要ありません。このようなメソッドがありますsetBackgroundImage
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];
}