23

I'm using a custom back button in my app. This custom back button is set globally like this:

    UINavigationBar.appearance().backIndicatorImage = UIImage(named: "Back").withRenderingMode(.alwaysOriginal)
    UINavigationBar.appearance().backIndicatorTransitionMaskImage = UIImage(asset: .back).withRenderingMode(.alwaysOriginal)

Before iOS 11 this code did the trick, but now in iOS 11 the button is not centered vertically anymore as you can see here:

enter image description here

I could change the height of the back button image to 44, but that would break it in iOS < 11. I could also use two different images, but I was looking for something cleaner, like a way to vertically center the image in the back button container view.

EDIT:

Turns out that, as said by banxii1988, the problem is caused by setBackButtonTitlePositionAdjustment when the values deliberately move the title outside the visible screen. That was an hack to avoid removing the back button title in every view controller. I decided to remove this hack and I did the right thing which is:

  1. set the back button item in the storyboard to " ".
  2. in each view controller without an associated storyboard, I set the backBarButtonItem programmatically navigationItem.backBarButtonItem = UIBarButtonItem(title: "", style: .plain, target: nil, action: nil)

Note that the back button title that you see in a view controller is set in the previous one in the navigation stack.

Luca Torella
  • 7,974
  • 4
  • 38
  • 48
  • I have the same issue, and after I inspected it with View Debugging. I found it's because we have using some trick at the BackButtonTitlePositionAdjustment to hide the backButtonTitle. so 1) navigationItem.title is required, the back Indicator has constraint to it, 2) remove tricky offset – banxi1988 Sep 14 '17 at 02:01

9 Answers9

12

1) remove PositionAdjustment if have any. such as

  bap.setBackButtonTitlePositionAdjustment(UIOffset(horizontal: 0, vertical: -64), for: .default)

2) check if the previous ViewController in nav stack has a title

banxi1988
  • 1,322
  • 13
  • 10
12

For anyone who couldn't resolve it:

1) Search in your project (cmd+shift+f) for "setBackButtonTitle", you will find a code like below:

[[UIBarButtonItem appearanceWhenContainedInInstancesOfClasses:@[[xController class]]] setBackButtonTitlePositionAdjustment:UIOffsetMake(0, -60) forBarMetrics:UIBarMetricsDefault];

2) Change code above with this one:

[[UIBarButtonItem appearanceWhenContainedInInstancesOfClasses:@[[xController class]]] setBackButtonTitlePositionAdjustment:UIOffsetMake(-200, 0) forBarMetrics:UIBarMetricsDefault];

Since iOS 11, above code we used to hide back button titles, moves back button along with the title itself. However if you just move the back button title horizontally, everything works fine and there's no backdraws.

asanli
  • 344
  • 2
  • 14
11

I think this method is ok! It's useful for me.

if(@available(iOS 11, *)) {
    [[UIBarButtonItem appearance] setTitleTextAttributes:@{NSForegroundColorAttributeName: [UIColor clearColor]} forState:UIControlStateNormal];
    [[UIBarButtonItem appearance] setTitleTextAttributes:@{NSForegroundColorAttributeName: [UIColor clearColor]} forState:UIControlStateHighlighted];

} else {
    [[UIBarButtonItem appearance] setBackButtonTitlePositionAdjustment:UIOffsetMake(-60, -60) forBarMetrics:UIBarMetricsDefault];
}
Tonin
  • 131
  • 7
  • This was the only solution that worked for me since I use the title of the View Controllers throughout the app. I couldn't remove the title because I would lose functionality. Just setting the text to be clear worked though. Thank you. – Nate4436271 Sep 28 '17 at 21:50
  • The only concern is button text (like "Cancel") is transparent (clear color) when share to other app (email, message, etc), flip color if need share feature: – Yao Li Nov 10 '17 at 19:31
  • My solution will effect all barButtonItems' titles. Titles' text-color will be cleared, eg:rightBarButtonItem, leftBarButtonItem. Solution by @asanli works well.But,when the sliding gesture returns slowly, you can see "offset the title of the screen" – Tonin Nov 13 '17 at 12:05
5

This solution works for me for iOS 9, 10 and 11

    var backButtonImage: UIImage = UIImage(named: "backButton")!
    UINavigationBar.appearance().backIndicatorImage = backButtonImage
    UINavigationBar.appearance().backIndicatorTransitionMaskImage = backButtonImage

    if #available(iOS 11, *) {
        UIBarButtonItem.appearance().setBackButtonTitlePositionAdjustment(UIOffsetMake(-300, 0), for:UIBarMetrics.default)
    } else {
        UIBarButtonItem.appearance().setBackButtonTitlePositionAdjustment(UIOffsetMake(0, -200), for:UIBarMetrics.default)
    }
Eugene
  • 223
  • 3
  • 6
3

After several tries and fails, this worked for us for iOS 11:

[[UIBarButtonItem appearance] setBackButtonTitlePositionAdjustment:UIOffsetMake(-200, -5) forBarMetrics:UIBarMetricsDefault];

The trick was to move the "Back" text on the x axis quite a lot back, and a bit on the y axis, since setBackButtonTitlePositionAdjustment turned out to be affecting both the image and the text.

However, we don't know how long this solution will work, it might be "fixed" by Apple at any time

effochka
  • 31
  • 2
1

i would recommend method swizzling to resolve it, i've used this solution in some of my projects and it works fine.

1 - create a category of UIViewController.

2- import #import <objc/runtime.h>.

3- paste the following method

#import "UIViewController+Extras.h"
#import <objc/runtime.h>
@implementation UIViewController (Extras)

+ (void)load {
    static dispatch_once_t once_token;
    dispatch_once(&once_token,  ^{
        SEL viewDidLoadSelector = @selector(viewDidLoad);
        SEL viewDidLoadModifyBackButtonSelector = @selector(modifyBackButton_viewDidLoad);
        Method originalMethod = class_getInstanceMethod(self, viewDidLoadSelector);
        Method extendedMethod = class_getInstanceMethod(self, viewDidLoadModifyBackButtonSelector);
        method_exchangeImplementations(originalMethod, extendedMethod);
    });
}

- (void)modifyBackButton_viewDidLoad {
    [self modifyBackButton_viewDidLoad];
    self.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"" style:self.navigationItem.backBarButtonItem.style target:nil action:nil];
}

@end
Mortgy
  • 543
  • 5
  • 9
1
if(@available(iOS 11, *)) {
    [[UIBarButtonItem appearance] setTitleTextAttributes:@{NSForegroundColorAttributeName: [UIColor clearColor]} forState:UIControlStateNormal];
    [[UIBarButtonItem appearance] setTitleTextAttributes:@{NSForegroundColorAttributeName: [UIColor clearColor]} forState:UIControlStateHighlighted];

} else {
    [[UIBarButtonItem appearance] setBackButtonTitlePositionAdjustment:UIOffsetMake(-60, -60) forBarMetrics:UIBarMetricsDefault];
}

The above solution by @Tonin works, but the only concern is nav bar left button text (like "Cancel") is transparent (clear color) when share to other app (email, message, etc), flip color if need share feature:

UIActivityViewController *activityViewController = [[UIActivityViewController alloc] initWithActivityItems:@[title, URL] applicationActivities:nil];
[activityViewController setCompletionWithItemsHandler:^(NSString *activityType, BOOL completed, NSArray *returnedItems, NSError *activityError) {
     // set clear color when back from share
     [[UIBarButtonItem appearance] setTitleTextAttributes:@{NSForegroundColorAttributeName: [UIColor clearColor]} forState:UIControlStateNormal];
}];
[self presentViewController:activityViewController animated:YES completion:^{
     // set white color when share to other app 
     [[UIBarButtonItem appearance] setTitleTextAttributes:@{NSForegroundColorAttributeName: [UIColor whiteColor]} forState:UIControlStateNormal];
}];
Yao Li
  • 2,071
  • 1
  • 25
  • 24
0

FOR SWIFT 3+

     if #available(iOS 11, *) {
            UIBarButtonItem.appearance().setTitleTextAttributes([NSForegroundColorAttributeName: UIColor.clear], for: .normal)
            UIBarButtonItem.appearance().setTitleTextAttributes([NSForegroundColorAttributeName: UIColor.clear], for: .highlighted)
     } else {
            UIBarButtonItem.appearance().setBackButtonTitlePositionAdjustment(UIOffsetMake(0,
-60), for:UIBarMetrics.default)
        }
MANISH PATHAK
  • 2,602
  • 4
  • 27
  • 31
0

1.

extension UINavigationController {
    func pushViewC(_ viewController: UIViewController, animated: Bool) {
        viewController.navigationItem.leftBarButtonItem = UIBarButtonItem(image: #imageLiteral(resourceName: "return").withRenderingMode(.alwaysOriginal), style: .plain, target: navigationController, action: #selector(popViewController(animated:)))
        pushViewController(viewController, animated: animated)
    }
}

2.use pushViewC instead of pushViewController

navigationController?.pushViewC(otherVC, animated: true)
BaQiWL
  • 417
  • 5
  • 14