UIBarButtonItem custom alignment with iOS7

So, I have the same problem that many others experience when creating a UIBarButtonItem with UIButton as a custom view.

Basically a button is about 10 pixels, far away either to the left or to the right. When I use a regular BarButtonItem without a custom view, this does not happen.

This post provided a partial solution: UIBarButton with custom view

Here is my code that I created by subclassing UIButton (as indicated in another post)

- (UIEdgeInsets)alignmentRectInsets { UIEdgeInsets insets; if ([self isLeftButton]) { insets = UIEdgeInsetsMake(0, 9.0f, 0, 0); } else { // IF ITS A RIGHT BUTTON insets = UIEdgeInsetsMake(0, 0, 0, 9.0f); } return insets; } - (BOOL)isLeftButton { return self.frame.origin.x < (self.superview.frame.size.width / 2); } 

This works fine, but when I exit the navigation controller from the navigation controller back to this main view, the button is still incorrectly positioned for about 0.3 seconds, and then it is inserted into the desired insert.

This is a HUGE eyeball, and I have no idea how to stop it from clicking. Any suggestions? Thank!

+17
ios7 uibutton uinavigationcontroller uibarbuttonitem
07 Oct '13 at 19:59
source share
4 answers

I had the same problem as you and many others. After a long time trying to fix this, I finally did it. This is the category that you should include in your * -Prefix.pch file. And it's all!

UINavigationItem + iOS7Spacing.h

 #import <Foundation/Foundation.h> @interface UINavigationItem (iOS7Spacing) @end 

UINavigationItem + iOS7Spacing.m

 #import "UINavigationItem+iOS7Spacing.h" #import <objc/runtime.h> @implementation UINavigationItem (iOS7Spacing) - (BOOL)isIOS7 { return ([[[UIDevice currentDevice] systemVersion] compare:@"7" options:NSNumericSearch] != NSOrderedAscending); } - (UIBarButtonItem *)spacer { UIBarButtonItem *space = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace target:nil action:nil]; space.width = -11; return space; } - (void)mk_setLeftBarButtonItem:(UIBarButtonItem *)leftBarButtonItem { if ([self isIOS7] && leftBarButtonItem) { [self mk_setLeftBarButtonItem:nil]; [self mk_setLeftBarButtonItems:@[[self spacer], leftBarButtonItem]]; } else { [self mk_setLeftBarButtonItem:leftBarButtonItem]; } } - (void)mk_setLeftBarButtonItems:(NSArray *)leftBarButtonItems { if ([self isIOS7] && leftBarButtonItems && leftBarButtonItems.count > 0) { NSMutableArray *items = [[NSMutableArray alloc] initWithCapacity:leftBarButtonItems.count + 1]; [items addObject:[self spacer]]; [items addObjectsFromArray:leftBarButtonItems]; [self mk_setLeftBarButtonItems:items]; } else { [self mk_setLeftBarButtonItems:leftBarButtonItems]; } } - (void)mk_setRightBarButtonItem:(UIBarButtonItem *)rightBarButtonItem { if ([self isIOS7] && rightBarButtonItem) { [self mk_setRightBarButtonItem:nil]; [self mk_setRightBarButtonItems:@[[self spacer], rightBarButtonItem]]; } else { [self mk_setRightBarButtonItem:rightBarButtonItem]; } } - (void)mk_setRightBarButtonItems:(NSArray *)rightBarButtonItems { if ([self isIOS7] && rightBarButtonItems && rightBarButtonItems.count > 0) { NSMutableArray *items = [[NSMutableArray alloc] initWithCapacity:rightBarButtonItems.count + 1]; [items addObject:[self spacer]]; [items addObjectsFromArray:rightBarButtonItems]; [self mk_setRightBarButtonItems:items]; } else { [self mk_setRightBarButtonItems:rightBarButtonItems]; } } + (void)mk_swizzle:(SEL)aSelector { SEL bSelector = NSSelectorFromString([NSString stringWithFormat:@"mk_%@", NSStringFromSelector(aSelector)]); Method m1 = class_getInstanceMethod(self, aSelector); Method m2 = class_getInstanceMethod(self, bSelector); method_exchangeImplementations(m1, m2); } + (void)load { [self mk_swizzle:@selector(setLeftBarButtonItem:)]; [self mk_swizzle:@selector(setLeftBarButtonItems:)]; [self mk_swizzle:@selector(setRightBarButtonItem:)]; [self mk_swizzle:@selector(setRightBarButtonItems:)]; } @end 

UINavigationItem + iOS7Spacing Category on GitHub

+53
Oct 11 '13 at
source share

Not a big fan of the UIButton subclass UIButton or the Marius swizzling method answer: https://stackoverflow.com/a/464677/

I just used a simple shell and moved the border of the x button in the negative direction until I found the correct positioning. Pressing a button also seems great (although you can increase the width of the button to fit a negative offset if you need to).

Here is the code I'm using to create a new return button:

 - (UIBarButtonItem *) newBackButton { // a macro for the weak strong dance @weakify(self); UIButton *backButton = [[UIButton alloc] init]; [backButton setTitle:@"Back" forState:UIControlStateNormal]; backButton.titleLabel.font = [UIFont systemFontOfSize:17]; CGFloat width = [@"Back" boundingRectWithSize:CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX) options:NSStringDrawingUsesFontLeading|NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName: [UIFont systemFontOfSize:17]} context:nil].size.width + 27; backButton.frame = CGRectMake(-17.5, 0, width + 17.5, 44); [backButton setImage:[UIImage imageNamed:@"UINavigationBarBackIndicatorDefault"] forState:UIControlStateNormal]; [backButton setTitleEdgeInsets:UIEdgeInsetsMake(0, 14, 0, 0)]; [backButton setTitleColor:mTCOrangeColor forState:UIControlStateNormal]; backButton.contentEdgeInsets = UIEdgeInsetsZero; backButton.imageEdgeInsets = UIEdgeInsetsZero; [backButton addEventHandler:^(id sender) { // a macro for the weak strong dance @strongify(self); // do stuff here } forControlEvents:UIControlEventTouchUpInside]; UIView *buttonWrapper = [[UIView alloc] initWithFrame:CGRectMake(0, 0, width, 44)]; [buttonWrapper addSubview:backButton]; return [[UIBarButtonItem alloc] initWithCustomView:buttonWrapper]; } 
+4
Jan 09 '14 at 19:57
source share

I have not tried this code for multiple navigationBarButton elements, but it seems to work for single buttons. I overtook UINavigationBar and its layoutSubviews method.

First, the horizontal offset constant is defined at the top of the file. Change as you wish:

 static CGFloat const kNavBarButtonHorizontalOffset = 10; 

layoutSubviews:

 - (void)layoutSubviews{ [super layoutSubviews]; //Do nothing on iOS6 if ( ![[[UIDevice currentDevice] systemVersion] floatValue] >= 7.0f){return;} UINavigationItem * navigationItem = [self topItem]; for(UIBarButtonItem * item in [navigationItem rightBarButtonItems]){ UIView * subview = [item customView]; CGFloat width = CGRectGetWidth(subview.frame); CGRect newRightButtonRect = CGRectMake(CGRectGetWidth(self.frame) - width - kNavBarButtonHorizontalOffset, CGRectGetMinY(subview.frame), width, CGRectGetHeight(subview.frame)); [subview setFrame:newRightButtonRect]; } for(UIBarButtonItem * item in [navigationItem leftBarButtonItems]){ UIView * subview = [item customView]; CGRect newRightButtonRect = CGRectMake(kNavBarButtonHorizontalOffset, CGRectGetMinY(subview.frame), CGRectGetWidth(subview.frame), CGRectGetHeight(subview.frame)); [subview setFrame:newRightButtonRect]; } } 
0
Mar 25 '14 at 15:07
source share

Alternatively, you can configure the frames of the contents of your user view (-5 for the left pane, +5 for the correct subzones), and as long as your user view does not have clipsToBounds set to YES then they will be executed. This is not the cleanest, but other suggested solutions may be even more hacker IMO.

Podcasts will be displayed at 5 points in IB, but all content is displayed when the application starts in the simulator.

0
Jun 25 '14 at 22:16
source share



All Articles