开发者

UINavigationBar topItem/items seems to double-pop on back

开发者 https://www.devze.com 2023-01-20 12:12 出处:网络
I am managing my own UINavigationBar. I need to do this due to extensive skinning. The documentation for UINavigationController warns that there are limitations to skinning the UINavigationBar when us

I am managing my own UINavigationBar. I need to do this due to extensive skinning. The documentation for UINavigationController warns that there are limitations to skinning the UINavigationBar when used with a UINavigationController.

I have put in extensive logging and from everything I can tell, pushing the "Back" button in the UINavigationController pops two items off of of the stack instead of one. I get a single delegate callback telling me that it is removing the logical item, but it actually removes that one and one more.

The item added to the UINavigationController in awakeFromNib should never be removed. It is being removed for some reason.

There are two similar questions, but neither have satisfactory answers. The two questions are:

UINavigationBar .items accessor doesn't return the current UINavigationItem

UINavigationBar seems to pop 2 items off stack on "back"

- (void)awakeFromNib {
    [headerView setDelegate: self];
    [headerView pushNavigationItem: tableDisplay animated: NO];
}

- (void) selectedStory: (NSNotification *)not {
    [headerView pushNavigationItem: base animated: NO];
    NSLog(@"Selected story: %@", base);
}

- (void) baseNav {
    NSLog(@"Current items: %@", [headerView items]);
    BaseInnerItem *current = (BaseInnerItem *)[headerView topItem];
    [self addSubview: [current view]];
}

- (BOOL)navigationBar: (UINavigationBar *)navigationBar shouldPushItem: (UINavigationItem *)item {
    return YES;
}

- (BOOL)navigationBar: (UINavigationBar *)navigationBar shouldPopItem: (UINavigationItem *)item {
    return YES;
}

- (void)navigationBar:(UINavigationBar *)navigationBar didPushItem:(UINavigationItem *)item {
    NSLog(@"didPushItem: %@", item);
    [self baseNav];
}

- (void)navigationBar:(UINavigationBar *)navigationBar didPopItem:(UINavigationItem *)item {
    NSLog(@"didPopItem: %开发者_如何学JAVA@", item);
    [self baseNav];
}

Edited to add relevant debugging from a single run:

2010-10-13 02:12:45.911 Remix2[17037:207] didPushItem: <TableDisplay: 0x5d41cc0>
2010-10-13 02:12:45.912 Remix2[17037:207] Current items: (
    "<TableDisplay: 0x5d41cc0>"
)
2010-10-13 02:12:49.020 Remix2[17037:207] didPushItem: <WebDisplay: 0x591a590>
2010-10-13 02:12:49.021 Remix2[17037:207] Current items: (
    "<TableDisplay: 0x5d41cc0>",
    "<WebDisplay: 0x591a590>"
)
2010-10-13 02:12:49.023 Remix2[17037:207] Selected story: <WebDisplay: 0x591a590>
2010-10-13 02:12:59.498 Remix2[17037:207] didPopItem: <WebDisplay: 0x591a590>
2010-10-13 02:12:59.499 Remix2[17037:207] Current items: (
)


You always have to call [super awakeFromNib] when your subclass implements that method, per the documentation for -awakeFromNib:

You must call the super implementation of awakeFromNib to give parent classes the opportunity to perform any additional initialization they require

Importantly, however, ...

I don't understand why you have to actually manage your own navigation bar. If you subclass UINavigationBar and only override certain drawing or layout methods such as -drawRect:, -layoutSubviews, etc., then all of the logic behind managing the navigation bar in a navigation controller will just fall back on the original UINaviationBar class.

I've had to do extensive view customization for almost every major UIKit class, but I always left the complicated logic to the original classes, overriding only drawing methods to customize the look and feel.

Incidentally, it's actually much easier to skin an entire app without subclassing at all if all you're doing is using custom image assets. By setting a layer's contents property, you can either customize the look and feel of a UIView-based class on an as-needed basis or throughout your entire app:

#import <QuartzCore/QuartzCore.h>
...
- (void)viewDidLoad
{
    [super viewDidLoad];

    UIImage * navigationBarContents = [UIImage imageNamed:@"navigation-bar"];
    self.navigationController.navigationBar.layer.contents =    
        (id)navigationBarContents.CGImage;
}

You can set the contents for any class that inherits from UIView: navigation bars, toolbars, buttons, etc. It's a lot easier to manage this way without having to subclass at all.


This appears to be a bug in the implementation of -[UINavigationBar items]

When called from inside the -navigationBar:didPopItem: delegate method, it will omit the last object. You can check this by calling [navigationBar valueForKey:@"_itemStack"] to retrieve the underlying array and see that the expected items are still there.

Adding a dispatch_async inside -navigationBar:didPopItem:method successfully works around the issue in my app.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号