2013-10-07 13 views
17

So habe ich das gleiche Problem, das viele andere beim Erstellen eines UIBarButtonItem mit einem UIButton als benutzerdefinierte Ansicht auftreten.Benutzerdefinierte UIBarButtonItem-Ausrichtung aus mit iOS7

Grundsätzlich ist die Schaltfläche etwa 10 Pixel zu weit links oder rechts. Wenn ich ein reguläres BarButtonItem ohne eine benutzerdefinierte Ansicht verwende, geschieht das nicht.

bereitgestellt Dieser Beitrag eine Teillösung: UIBarButton With Custom View

Hier ist mein Code, den ich von Subklassen UIButton erstellt haben (wie im anderen Beitrag erwähnt)

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

Dies funktioniert gut, aber wenn ich Pop ein View-Controller vom Navigations-Controller zurück zu dieser Hauptansicht, der Button ist für ca. 0,3 Sekunden immer noch falsch positioniert, und dann rastet er in den korrekten Inset ein.

Dies ist ein großer Schandfleck und ich habe keine Ahnung, wie man es davon abhält, so zu schnappen. Irgendwelche Gedanken? Vielen Dank!

Antwort

53

Ich hatte das gleiche Problem wie du und viele andere. Nachdem ich lange versucht habe, es zu beheben, habe ich es endlich geschafft. Dies ist die Kategorie, die Sie in Ihre * -Prefix.pch-Datei aufnehmen müssen. Und das ist alles!

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

+0

Ich denke, das ist die beste Lösung, besser als UIEdegeInset ändern, das hat kein Gewindebereich Problem – Nick

+0

Sehr gut! Das hat mein Problem auch gelöst! – rockstarberlin

+0

Perfekte Lösung. Wenn ich könnte, würde ich es 10 Mal upvote. – kufi

4

kein großer Fan von Subklassifizieren UIButton oder Methode Swizzling von Marius Antwort: https://stackoverflow.com/a/19317105/287403

Ich habe gerade einen einfachen Wrapper verwendet und das x des Button-Frames in eine negative Richtung verschoben, bis ich die richtige Position gefunden habe. Das Antippen von Schaltflächen scheint ebenfalls in Ordnung zu sein (obwohl Sie die Breite der Schaltfläche erweitern könnten, um sie an den negativen Offset anzupassen, wenn Sie sie benötigen).

Hier ist der Code, den ich eine neue Back-Button erzeugen verwenden:

- (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]; 
} 
+0

Dies ist die einzige Lösung, die für mich funktionierte, und es wird die Lösung für jeden sein, der UIButton als Unteransicht (+1) verwendet – Yossi

0

ich nicht diesen Code für mehrere navigationBarButton Artikel versucht, aber es scheint, für einzelne Tasten zu arbeiten. Ich habe UINavigationBar und seine layoutSubviews-Methode überschrieben.

Zuerst wird eine Konstante für den horizontalen Versatz am Anfang der Datei definiert. Ändern Sie ist, wie Sie wollen:

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

Alternativ können Sie den Rahmen des Inhalts Ihrer benutzerdefinierte Ansicht (-5 für die linke Subviews, 5 für den richtigen Subviews einstellen) und solange Ihre benutzerdefinierte Ansicht clipsToBounds nicht auf YES festgelegt hat, werden diese berücksichtigt. Es ist nicht das sauberste, aber andere vorgeschlagene Lösungen können noch hacky IMO sein.

Die Unteransichten werden in IB um 5 Punkte abgeschnitten angezeigt, aber der gesamte Inhalt wird angezeigt, wenn Sie die App im Simulator ausführen.