Set programmatically a custom subclass of UINavigationBar in UINavigationController

本文将介绍如何在Swift中使用自定义子类来替换默认的UINavigationBar,并通过archive和unarchive方法实现这一目标,从而在不使用Interface Builder的情况下定制导航栏样式。

源自:http://stackoverflow.com/questions/1869331/set-programmatically-a-custom-subclass-of-uinavigationbar-in-uinavigationcontrol


问:

Does anyone know how can I use my custom subclass of UINavigationBar if I instantiateUINavigationController programmatically (without IB)?

Drag a UINavigationController in IB show me an under Navigation Bar and using Identity Inspectory I can change class type and set my own subclass of UINavigationBar but programmatically I can't, navigationBar property of Navigation Controller is readonly...

What should I do to customize the navigation bar programmatically? Is IB more "powerful" than "code"? I believed all that can be done in IB could be done also programmatically.




回答1:

As far as I can tell, sometimes it is indeed necessary to subclass UINavigationBar, to do some nonstandard restyling. It's sometimes possible to avoid having to do so by using categories, but not always.

Currently, as far as I know, the only way to set a custom UINavigationBar within a UIViewController is via IB (that is, via an archive) - it probably shouldn't be that way, but for now, we gotta live with it.

This is often fine, but sometimes using IB isn't really feasible.

So, I saw three options:

  1. Subclass UINavigationBar and hook it all up in IB, then muck about loading up the nib each time I wanted a UINavigationController,
  2. Use method replacement within a category to change the behaviour of UINavigationBar, rather than subclassing, or
  3. Subclass UINavigationBar and do a little mucking about with archiving/unarchiving the UINavigationController.

Option 1 was unfeasible (or at least too annoying) for me in this case, as I needed to create the UINavigationController programmatically, 2 is a little dangerous and more of a last-resort option in my opinion, so I chose option 3.

My approach was to create a 'template' archive of a UINavigationController, and unarchive that, returning it in initWithRootViewController.

Here's how:

In IB, I created a UINavigationController with the appropriate class set for the UINavigationBar.

Then, I took the existing controller, and saved an archived copy of it using +[NSKeyedArchiver archiveRootObject:toFile:]. I just did this within the app delegate, in the simulator.

I then used the 'xxd' utility with the -i flag, to generate c code from the saved file, to embed the archived version in my subclass (xxd -i path/to/file).

Within initWithRootViewController I unarchive that template, and set self to the result of the unarchive:

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

Then, I can just grab a new instance of my UIViewController subclass which has the custom navigation bar set:

UIViewController *modalViewController = [[[CTNavigationController alloc] initWithRootViewController:myTableViewController] autorelease];
[self.navigationController presentModalViewController:modalViewController animated:YES];

This gives me a modal UITableViewController with a navigation bar and toolbar all set up, and with the custom navigation bar class in place. I didn't need to do any slightly-nasty method replacement, and I don't have to muck about with nibs when I really just want to work programmatically.

I would like to see the equivalent of +layerClass within UINavigationController -+navigationBarClass - but for now, this works.


回答2:

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

【激光质量检测】利用丝杆与步进电机的组合装置带动光源的移动,完成对光源使用切片法测量其光束质量的目的研究(Matlab代码实现)内容概要:本文研究了利用丝杆与步进电机的组合装置带动光源移动,结合切片法实现对激光光源光束质量的精确测量方法,并提供了基于Matlab的代码实现方案。该系统通过机械装置精确控制光源位置,采集不同截面的光强分布数据,进而分析光束的聚焦特性、发散角、光斑尺寸等关键质量参数,适用于高精度光学检测场景。研究重点在于硬件控制与图像处理算法的协同设计,实现了自动化、高重复性的光束质量评估流程。; 适合人群:具备一定光学基础知识和Matlab编程能力的科研人员或工程技术人员,尤其适合从事激光应用、光电检测、精密仪器开发等相关领域的研究生及研发工程师。; 使用场景及目标:①实现对连续或脉冲激光器输出光束的质量评估;②为激光加工、医疗激光、通信激光等应用场景提供可靠的光束分析手段;③通过Matlab仿真与实际控制对接,验证切片法测量方案的有效性与精度。; 阅读建议:建议读者结合机械控制原理与光学测量理论同步理解文档内容,重点关注步进电机控制逻辑与切片数据处理算法的衔接部分,实际应用时需校准装置并优化采样间距以提高测量精度。
In Android development, encapsulating a website typically refers to creating an interface that allows you to interact with the website's content without exposing its underlying implementation details or hosting the entire site within your app. This is often done using webviews and APIs. Here's a basic explanation: 1. **WebView**: WebView is a built-in component that lets you display HTML, CSS, and JavaScript content inside an Android app. You can load websites by setting the WebView's `loadUrl()` method with the desired URL. ```java WebView webView = findViewById(R.id.web_view); webView.loadUrl("https://example.com"); ``` 2. **Content Providers (optional)**: If you need to access specific data from the website, you might consider using a Content Provider to fetch that information. For instance, if the website offers JSON data, you can create a custom content provider for your app to consume. 3. **API Integration**: If the website has an API, use Android's HTTP clients like `HttpClient` or later `OkHttp`, or even better, with Retrofit for a more structured approach. This allows you to make calls to the server programmatically. 4. **Separation of Concerns**: To maintain encapsulation, separate the code responsible for interacting with the website (the "adapter" layer) from your app's core logic. This helps keep your app clean and maintainable. ```java // Using Retrofit for API integration Retrofit retrofit = new Retrofit.Builder() .baseUrl("https://api.example.com/") .addConverterFactory(GsonConverterFactory.create()) .build(); ApiService service = retrofit.create(ApiService.class); Call<ApiResponse> call = service.getData(); call.enqueue(new Callback<ApiResponse>() { @Override public void onResponse(Call<ApiResponse> call, Response<ApiResponse> response) { // Handle API response } @Override public void onFailure(Call<ApiResponse> call, Throwable t) { // Handle errors } }); ``` **
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值