Frage UIBarButtonItem mit benutzerdefinierter Ansicht, die auf iOS 7 nicht ordnungsgemäß ausgerichtet ist, wenn sie als linke oder rechte Navigationsleistenelemente verwendet wird


Der folgende Code funktioniert über iOS 6:

UIButton *myButton = nil;
myButton = [UIButton buttonWithType:UIButtonTypeCustom];
myButton.bounds = CGRectMake(0,0,44,30);
// setup myButton's images, etc.

UIBarButtonItem *item = nil;
item = [[UIBarButtonItem alloc] initWithCustomView:customButton];

So soll die Schaltfläche ausgerichtet sein:

Normal positioning

Auf iOS 7 scheint die Schaltfläche jedoch um zu viele Pixel von rechts oder links versetzt zu sein:

Incorrect positioning on iOS 7

Wie kann ich erreichen, dass meine benutzerdefinierten Bar-Schaltflächen korrekt ausgerichtet werden?


75
2017-09-17 22:54


Ursprung


Antworten:


Funktioniert bis iOS11!

Sie können negative flexible Leerzeichen und die rightBarButtonItems-Eigenschaft anstelle von rightBarButtonItem verwenden:

UIBarButtonItem *spacer = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace target:nil action:nil];
spacer.width = -10; // for example shift right bar button to the right

self.navigationItem.rightBarButtonItems = @[spacer, yourBarButton];

66
2018-04-04 16:23



Um diesen Fehler zu beheben, müssen Sie die UIButton-Klasse ableiten, damit Sie sie überschreiben können alignmentRectInsets. Von meinen Tests müssen Sie ein UIEdgeInsets entweder mit einem positiven rechten Offset oder einem positiven linken Offset zurückgeben, abhängig von der Position der Schaltfläche. Diese Zahlen ergeben für mich keinen Sinn (mindestens einer von ihnen sollte nach dem gesunden Menschenverstand negativ sein), aber das ist, was tatsächlich funktioniert:

- (UIEdgeInsets)alignmentRectInsets {
    UIEdgeInsets insets;
    if (IF_ITS_A_LEFT_BUTTON) {
        insets = UIEdgeInsetsMake(0, 9.0f, 0, 0);
    } 
    else { // IF_ITS_A_RIGHT_BUTTON
        insets = UIEdgeInsetsMake(0, 0, 0, 9.0f);
    }
    return insets;
}

Besonderer Dank geht an @zev für den Vorschlag, dass ich versuche, alignmentRectInsets anzupassen.


57
2017-09-17 22:54



Ich habe alle obigen Antworten ausprobiert und nichts hat für mich funktioniert. Und hier ist, was funktioniert, wenn jemand das brauchen würde:

(Kein Unterklassen benötigt)

// Add your barButtonItem with custom image as the following 
UIBarButtonItem *barButton = [[UIBarButtonItem alloc] initWithTitle:@"" style:UIBarButtonItemStyleBordered target:self action:@selector(categoryButtonPressed)];
// set your custom image
[barButton setImage:categoryImage];
// finally do the magic
barButton.imageInsets = UIEdgeInsetsMake(0.0, -20, 0, 0);

- Sehen Sie sich das Ergebnis an.

Beachten Sie das Leerzeichen auf der linken Seite und von der rechten Taste (die   rechte Taste hat das Standardverhalten)

enter image description here


28
2017-11-13 10:16



Ok, ich ging die andere "Richtung". Ich habe alles richtig über Storyboard mit iOS 7 ausgerichtet (vorausgesetzt, dass es so weiter geht). Und dann benutze ich den Subklassen-Ansatz beschreiben, ich Unterklasse UIButton mit der folgenden Implementierung.

- (UIEdgeInsets)alignmentRectInsets {
    UIEdgeInsets insets;
    if (SYSTEM_VERSION_LESS_THAN(@"7.0")) {
        if ([self isLeftButton]) {
            insets = UIEdgeInsetsMake(0, -10, 0, 0);
        } else {
            insets = UIEdgeInsetsMake(0, 0, 0, -10);
        }
    } else {
        insets = UIEdgeInsetsZero;
    }

    return insets;
}

- (BOOL)isLeftButton {
    return self.frame.origin.x < (self.superview.frame.size.width / 2);
}

Dieser Code läuft nur, wenn das Gerät vor iOS 7 läuft.

Danke für die Einsicht @jaredsinclair!


10
2017-09-19 22:55



@jaredsinclair

Hier ist ein Blick auf meinen Code.

// Create the tweetly button that will show settings
self.tweetlyDisplay = [NavButton buttonWithType:UIButtonTypeCustom];
[self.tweetlyDisplay setFrame:CGRectMake(0, 0, 90, 44)];
[self.tweetlyDisplay setBackgroundColor:[UIColor clearColor]];
[self.tweetlyDisplay setBackgroundImage:[UIImage imageNamed:@"settings.png"] forState:UIControlStateNormal];
[self.tweetlyDisplay setAdjustsImageWhenHighlighted:NO];
[self.tweetlyDisplay addTarget:self action:@selector(tweetlyPressed:) forControlEvents:UIControlEventTouchUpInside];

// Add the Tweetly button as the left bar button item
// This had a glitch that moves the image to the right somewhat
UIBarButtonItem *leftBarButton = [[UIBarButtonItem alloc] initWithCustomView:self.tweetlyDisplay];
self.navigationItem.leftBarButtonItem = leftBarButton;

Siehst du etwas, das nicht stimmt?

Hier ist das Ergebnis. Das zweite Bild ist nicht so sichtbar, weil ich den Screenshot mit der Zeit aufnehmen musste und es sich immer noch im Übergang befindet, aber Sie können deutlich sehen, wie es falsch versetzt ist.

Gutes normales Bild:

This is the good image, before navigating around

Schlechtes Offset-Bild:

You can see the offset is now off again before snapping into place

Nach etwa einer halben Sekunde springt das Bild zurück zum ursprünglichen Bildort.

Hier ist mein Code für NavButton.h und .m:

/**********************************************

 NavButton.h

 **********************************************/

#import <UIKit/UIKit.h>

@interface NavButton : UIButton

@end






/**********************************************

 NavButton.m

**********************************************/


#import "NavButton.h"

@implementation NavButton {

    int imageHeight;

}

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        // Initialization code

        imageHeight = 44;

    }
    return self;
}

/*
 // Only override drawRect: if you perform custom drawing.
 // An empty implementation adversely affects performance during animation.
 - (void)drawRect:(CGRect)rect
 {
 // Drawing code
 }
 */


- (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 IS THE TRICK.  We make the height of the background rect match the image.
-(CGRect)backgroundRectForBounds:(CGRect)bounds
{
    CGRect bgRect = bounds;
    bgRect.origin.y = (bounds.size.height - imageHeight)/2.0f;
    bgRect.size.height = imageHeight;

    return bgRect;
}


@end

3
2017-10-11 19:44



Eine bessere Lösung ohne Sprungtasten gibt es hier:

Benutzerdefinierte UIBarButtonItem-Ausrichtung mit iOS7 deaktiviert


2
2017-10-29 18:29



Kein großer Fan von Unterklassen UIButton oder Methode, die aus Marius 'Antwort schlüpft: https://stackoverflow.com/a/19317105/287403

Ich benutzte nur einen einfachen Wrapper und bewegte das x des Rahmens des Buttons in eine negative Richtung, bis ich die richtige Positionierung fand. 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 verwende, um einen neuen Zurück-Button zu generieren:

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

1
2018-01-09 19:57



Ich möchte zu @jaredsinclair bestätige beantworten die folgenden Methoden überschreiben, die Text und / oder Bild in ihrer Navigationsschaltfläche haben, sonst werden der Text und das Bild nicht ausgerichtet (nur das Hintergrundbild):

- (UIEdgeInsets) titleEdgeInsets {
if(SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"7.0"))
{
    if (IF_ITS_A_LEFT_BUTTON) {
    {
        return UIEdgeInsetsMake(0, 0, 0, 9.0f);
    }
    else
    { // IF_ITS_A_RIGHT_BUTTON
        return UIEdgeInsetsMake(0, 9.0f, 0, 0);
    }
}
return [super titleEdgeInsets];
}

- (UIEdgeInsets)imageEdgeInsets {
if(SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"7.0"))
{
    if (IF_ITS_A_LEFT_BUTTON)
    {
        return UIEdgeInsetsMake(0, 0, 0, 9.0f);
    }
    else
    { // IF_ITS_A_RIGHT_BUTTON
        return UIEdgeInsetsMake(0, 9.0f, 0, 0);
    }
}

return [super imageEdgeInsets];
}

p.s. das Makro ist:

#define SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(v)  ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedAscending)

1
2018-01-26 20:50



*Lösung gefunden, lesen Sie am Ende der Antwort.*

@jaredsinclair

Ich habe einen ähnlichen Fall wie Kyle Begeman

Dies ist der Knopf

 - (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        self.contentHorizontalAlignment = UIControlContentHorizontalAlignmentCenter;
    }
    return self;
}

- (UIEdgeInsets)alignmentRectInsets {
    UIEdgeInsets insets;

    if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"7.0")){
        if ([self isLeftButton]) {
            insets = UIEdgeInsetsMake(0, 9.0f, 0, 0);
        } else {
            insets = UIEdgeInsetsMake(0, 0, 0, 9.0f);
        }
    }else{
        insets = UIEdgeInsetsZero;
    }

    return insets;
}

- (BOOL)isLeftButton {
    return self.frame.origin.x < (self.superview.frame.size.width / 2);
}

und ich benutze es in diesem Fall

PBNavigationBarButton *dashboardButton = [PBNavigationBarButton buttonWithType:UIButtonTypeCustom];
    UIImage *dashboardImage = [UIImage imageNamed:@"btn_dashboard.png"];
    UIImage *dashboardImageHighlighted = [UIImage imageNamed:@"btn_dashboard_pressed.png"];

    NSInteger halfImageWidth = ceilf([dashboardImage size].width/2.0f);

    [dashboardButton setBackgroundImage:[dashboardImage stretchableImageWithLeftCapWidth:halfImageWidth topCapHeight:0] forState:UIControlStateNormal];
    [dashboardButton setBackgroundImage:[dashboardImageHighlighted stretchableImageWithLeftCapWidth:halfImageWidth topCapHeight:0] forState:UIControlStateHighlighted];
    [dashboardButton addTarget:target action:action forControlEvents:UIControlEventTouchUpInside];
    [dashboardButton sizeToFit];

    UIBarButtonItem *barButtonItem = [[UIBarButtonItem alloc]  initWithCustomView:dashboardButton];

Alles sieht gut aus, außer dass, wenn ich zurück zum vorherigen VC gehe, der Button sich selbst neu positioniert. Wie ein kleiner Sprung. Für mich springt es gleich nach einer Sekunde nicht. Und es passiert, nachdem ich den popVC von NavigationViewController mache.

Edit: Die Antwort unten, die swizzling Methode von Marius hat mir auch geholfen. 


0
2017-10-15 07:40



Ich kam mit einer verkürzten Version von Jaredsinclairs Ansatz auf:

- (UIEdgeInsets)alignmentRectInsets {
    return [self isLeftButton] ? UIEdgeInsetsMake(0, 9.0f, 0, 0) : UIEdgeInsetsMake(0, 0, 0, 9.0f); 
}

- (BOOL)isLeftButton {
    return self.frame.origin.x < (self.superview.frame.size.width / 2);
}

Klappt wunderbar.


0
2018-02-05 15:15



In ios7 können Sie einfach ein dummy barbuttonitem hinzufügen Um den linken Platz zu fixieren, sollten Sie Dummy als erstes, rechts als letztes hinzufügen

Beispiel für links, sollten Sie dies nach dem Festlegen Ihrer ursprünglichen Elemente oder in viewdidload hinzufügen, wenn Sie Schaltflächen mit Storyboard festlegen.

NSMutableArray *buttons = [[NSMutableArray alloc] init];
UIBarButtonItem *spacerItem = [[UIBarButtonItem alloc] init];
[buttons addObject:spacerItem];
for(UIBarButtonItem *item in self.leftBarButtonItems){
    [buttons addObject:item];
}
[self setLeftBarButtonItems:[NSArray arrayWithArray:buttons] animated:NO];

0
2018-02-26 16:45