Rectangle 27 0

cocoa touch iOS 7 custom back button?


- (void)loadView
{
    [super loadView];

    UIButton *backButton = [[UIButton alloc] initWithFrame: CGRectMake(0, 0, 60.0f, 30.0f)];
    UIImage *backImage = [[UIImage imageNamed:@"back_button_normal.png"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 12.0f, 0, 12.0f)];
    [backButton setBackgroundImage:backImage  forState:UIControlStateNormal];
    [backButton setTitle:@"Back" forState:UIControlStateNormal];
    [backButton addTarget:self action:@selector(popBack) forControlEvents:UIControlEventTouchUpInside];
    UIBarButtonItem *backButtonItem = [[UIBarButtonItem alloc] initWithCustomView:backButton];
    self.navigationItem.leftBarButtonItem = backButtonItem;
}

-(void) popBack {
  [self.navigationController popViewControllerAnimated:YES];
}
self.navigationController.interactivePopGestureRecognizer.delegate = (id<UIGestureRecognizerDelegate>)self;

If you still want you back button have the same as it looked in iOS6 than you should probably create those back buttons manually:

Thanks for reply. Unfortunately it is too expensive to manage back button using leftBarButtonItem in existing project, so we decided to follow the guideline and remove background images for bar buttons in iOS 7. Looks really nice.

This is not a bug, this how Back button looks in iOS 7. For example:

Too extensive. You don't need to init itwith custom button. Things can be done easier by customizing appearance of this single BarButtonItem to look like good ol' back button.

You should probably use the new concept for your application, and not to set background image for back button in iOS 7.

if u use this method the swipe back gesture doesn't work

Note
Rectangle 27 0

cocoa touch iOS 7 custom back button?


if ([UINavigationBar instancesRespondToSelector:@selector(setBackIndicatorImage:)]) { // iOS 7
    [navigationBarAppearance setBackIndicatorImage:[UIImage imageNamed:@"transparent_1px"]];
    [navigationBarAppearance setBackIndicatorTransitionMaskImage:[UIImage imageNamed:@"transparent_1px"]];
}

The custom background image not appearing on the first push was fixed in iOS 7 GM.

Note
Rectangle 27 0

cocoa touch iOS 7 custom back button?


#import <objc/runtime.h>

@implementation UINavigationBar (BackButtonDisplayFix)

+ (void)load
{
    if ([UIDevice currentDevice].systemVersion.intValue >= 7)
    {
        /*
         * We first try to simply add an override version of didAddSubview: to the class.  If it
         * fails, that means that the class already has its own override implementation of the method
         * (which we are expecting in this case), so use a method-swap version instead.
         */
        Method didAddMethod = class_getInstanceMethod(self, @selector(_displaybugfixsuper_didAddSubview:));
        if (!class_addMethod(self, @selector(didAddSubview:),
                             method_getImplementation(didAddMethod),
                             method_getTypeEncoding(didAddMethod)))
        {
            Method existMethod = class_getInstanceMethod(self, @selector(didAddSubview:));
            Method replacement = class_getInstanceMethod(self, @selector(_displaybugfix_didAddSubview:));
            method_exchangeImplementations(existMethod, replacement);
        }
    }
}

- (void)_displaybugfixsuper_didAddSubview:(UIView *)subview
{
    [super didAddSubview:subview];
    [subview setNeedsDisplay];
}

- (void)_displaybugfix_didAddSubview:(UIView *)subview
{
    [self _displaybugfix_didAddSubview:subview]; // calls the existing method
    [subview setNeedsDisplay];
}

@end

Additional note: This bug appears to be fixed in iOS 7.1. So, the patch could be conditionalized to only install the methods if running >= 7.0 and < 7.1.

As mentioned, ideally you would be able to change to a borderless look on iOS7, which means that the bug isn't really apparent (since there is no background image). For some iOS6/iOS7 transition situations though, that may be difficult (lots of screens, and/or the need to support older iOS versions for a while and too hard to have two looks implemented, and it doesn't look good borderless without other changes). If that's the case, the following patch should work:

Note: UINavigationBar does currently have an override of the method in question, so I'd expect the method_exchangeImplementations style to be used. I just added the other stuff for safety in case Apple changes their code. We may go borderless ourselves, but I did find this approach worked as an option (until a more thorough UI uplift), at least.

Thanks! I use this by subclassing the UINavigationBar and overriding the didAddSubview.

The custom background image not appearing initially was not fixed in iOS7 GM or final, as far as I can tell. I see the same problem. It does seem to be an Apple bug; the private view Apple uses simply does not get a setNeedsDisplay call when it needs it on initial display. Doing anything to it which causes that call should fix it -- like pressing on it (which likely changes internal state so it calls setNeedsDisplay on itself), or bringing a modal up (which probably forces a redisplay of the entire view hierarchy on the next viewWillAppear: call).

Using leftBarItems instead also can work, but that may cause a lot of maintenance issues with existing code (some screens may have their own left items, expecting that when set back to nil they restore the original back item, for example).

Note
Rectangle 27 0

cocoa touch iOS 7 custom back button?


// set custom NavagationBar for back button position
[self.navigationController setValue:[[SANavigationBar alloc] init] forKey:@"navigationBar"];
UINavigationBar
[UINavigationBar appearance].backIndicatorImage = [[UIImage imageNamed:@"topbar_icon_back_n.png"] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
[UINavigationBar appearance].backIndicatorTransitionMaskImage = [[UIImage imageNamed:@"topbar_icon_back_p.png"] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
_UINavigationBarBackIndicatorView
backIndicatorImage
const CGPoint SANavigationBarOffset = {-8, 11.5};

@implementation SANavigationBar

- (void)layoutSubviews
{
    [super layoutSubviews];

    // set back button position
    NSArray *classNamesToReposition = @[@"_UINavigationBarBackIndicatorView"];

    for (UIView *view in [self subviews]) {
        if ([classNamesToReposition containsObject:NSStringFromClass([view class])]) {
            CGRect frame = [view frame];
            frame.origin.x = 0;
            frame.origin.y = 0;

            [view setFrame:frame];
        }
    }
}

@end
self.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"" style:UIBarButtonItemStylePlain target:nil action:nil];

Thanks a lot! The first two steps are enough to change the default arrow image and hide the back button text.

then, set default backIndicatorImage of back button using custom image.

Note
Rectangle 27 0

cocoa touch iOS 7 custom back button?


- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated {
dispatch_async(dispatch_get_main_queue(), ^{
    [[navigationController.navigationBar subviews] makeObjectsPerformSelector:@selector(setNeedsDisplay)];
});

There is a better solution that doesn't involve method swizzling.

You need to add UINavigationViewControllerDelegate method somewhere in your app.

Note
Rectangle 27 0

cocoa touch iOS 7 custom back button?


UIButton *btnAdd = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 60, 30)];

        [btnAdd setContentMode:UIViewContentModeScaleAspectFit];

        [btnAdd setBackgroundImage:[UIImage imageNamed:@"back.png"] forState:UIControlStateNormal];

        [btnAdd addTarget:self action:@selector(backButtonPressed:) forControlEvents:UIControlEventTouchUpInside];

        UIBarButtonItem *btnAdd = [[UIBarButtonItem alloc] initWithCustomView:imView];

        self.navigationItem.rightBarButtonItem = btnAdd;

Add button as navigation item in ios7 as below

Note
Rectangle 27 0

cocoa touch iOS 7 custom back button?


extension UIViewController: UIGestureRecognizerDelegate {
    func popBack() {
        self.navigationController?.popViewControllerAnimated(true)
    }

    func enableCustomBackButtom() {
        self.navigationItem.leftBarButtonItem = UIBarButtonItem(image: UIImage(named: "icon-back"), style: UIBarButtonItemStyle.Plain, target: self, action:"popBack")

        self.navigationController?.interactivePopGestureRecognizer.delegate = self
    }
}
self.enableCustomBackButtom()

And in your UIViewController use like this:

Using Swift you can just add a extension:

Note
Rectangle 27 0

cocoa touch iOS 7 custom back button?


UIButton *btnAdd = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 60, 30)];

        [btnAdd setContentMode:UIViewContentModeScaleAspectFit];

        [btnAdd setBackgroundImage:[UIImage imageNamed:@"back.png"] forState:UIControlStateNormal];

        [btnAdd addTarget:self action:@selector(backButtonPressed:) forControlEvents:UIControlEventTouchUpInside];

        UIBarButtonItem *btnAdd = [[UIBarButtonItem alloc] initWithCustomView:imView];

        self.navigationItem.rightBarButtonItem = btnAdd;

Add button as navigation item in ios7 as below

Note
Rectangle 27 0

cocoa touch iOS 7 custom back button?


- (void)loadView
{
    [super loadView];

    UIButton *backButton = [[UIButton alloc] initWithFrame: CGRectMake(0, 0, 60.0f, 30.0f)];
    UIImage *backImage = [[UIImage imageNamed:@"back_button_normal.png"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 12.0f, 0, 12.0f)];
    [backButton setBackgroundImage:backImage  forState:UIControlStateNormal];
    [backButton setTitle:@"Back" forState:UIControlStateNormal];
    [backButton addTarget:self action:@selector(popBack) forControlEvents:UIControlEventTouchUpInside];
    UIBarButtonItem *backButtonItem = [[UIBarButtonItem alloc] initWithCustomView:backButton];
    self.navigationItem.leftBarButtonItem = backButtonItem;
}

-(void) popBack {
  [self.navigationController popViewControllerAnimated:YES];
}
self.navigationController.interactivePopGestureRecognizer.delegate = (id<UIGestureRecognizerDelegate>)self;

If you still want you back button have the same as it looked in iOS6 than you should probably create those back buttons manually:

Thanks for reply. Unfortunately it is too expensive to manage back button using leftBarButtonItem in existing project, so we decided to follow the guideline and remove background images for bar buttons in iOS 7. Looks really nice.

This is not a bug, this how Back button looks in iOS 7. For example:

Too extensive. You don't need to init itwith custom button. Things can be done easier by customizing appearance of this single BarButtonItem to look like good ol' back button.

You should probably use the new concept for your application, and not to set background image for back button in iOS 7.

if u use this method the swipe back gesture doesn't work

Note
Rectangle 27 0

cocoa touch iOS 7 custom back button?


- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated {
dispatch_async(dispatch_get_main_queue(), ^{
    [[navigationController.navigationBar subviews] makeObjectsPerformSelector:@selector(setNeedsDisplay)];
});

There is a better solution that doesn't involve method swizzling.

You need to add UINavigationViewControllerDelegate method somewhere in your app.

Note