UIBarButtonItem із користувацьким поданням, неправильно вирівняним на iOS 7, коли використовується як лівий або правий елементи панелі навігації


75

У 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];

Ось як слід вирівняти кнопку:

Нормальне позиціонування

Однак у iOS 7 кнопка здається зміщеною праворуч або ліворуч на занадто багато пікселів:

Неправильне позиціонування на iOS 7

Як я можу забезпечити належне вирівнювання елементів користувацьких кнопок панелі?


4
Можливо, моє рішення вам допоможе: Як виправити інтервал iOS 7 UIBarButtonItem
Marius Kažemėkaitis

Відповіді:


67

Працює до iOS11!

Ви можете використовувати негативні гнучкі пробіли та властивість rightBarButtonItems замість rightBarButtonItem:

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

2
Я не можу зрозуміти, чому ми маємо зламати щось таке просте ... Це існує як мінімум рік. Немає рішення Apple?
Петро

3
Це порушує керівництво по людському інтерфейсу. Ось чому він не налаштовується. Вони сподіваються, що ви залишите це в спокої. Це проблема з дизайнерами, які не читають рекомендації.
smileBot

Хоча я попередив друга дизайнера про подібні речі, я можу підтвердити, що це працює. Тепер ми маємо негабаритний аксесуарView.
Буде

1
Працює як шарм. І це єдиний спосіб, який я міг знайти для роботи у всіх системах iOS 6 ~ 8 без підкласифікації UINavigationBar. Чудова відповідь!
liuyaodong

Так. Apple змінює макет панелі навігації.
malex

57

Для того, щоб виправити цю помилку, ви повинні підклас UIButton, щоб ви могли замінити alignmentRectInsets. З мого тестування вам потрібно буде повернути UIEdgeInsets або з позитивним правим зсувом, або з позитивним зсувом ліворуч, залежно від положення кнопки. Ці цифри для мене не мають сенсу (принаймні один із них повинен бути від’ємним, згідно із здоровим глуздом), але насправді це працює:

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

Особлива подяка @zev за пропозицію спробувати відрегулювати alignmentRectInsets.


1
Чи потрібно застосовувати цей код лише на iOS7? А як щодо iOS6? Дякую.
Рікардо

Кріс: У своєму коді я встановлюю позицію, коли виділяю / запускаю кнопку. Оскільки це все одно підклас, я додав спеціальний @propertyдля позиції, лівий чи правий. Рікардо: це лише для iOS 7.
jaredsinclair

3
Це працює, але коли я переходжу до подання зі стеку навігації, і він анімує "назад", кнопка все ще неправильно зміщується протягом приблизно півсекунди, після чого вона переходить у правильне місце. Будь-яка ідея, як уникнути цього?
Kyle Begeman

У мене така сама проблема, коли я це реалізував. Тепер стрибки моїх кнопок формують своє положення. Я використовував та цю відповідь stackoverflow.com/questions/15261148/…, але це мені також не допомогло. Будь-які ідеї?
Лілі

@KyleBegeman & Lily - Я не бачу такої поведінки на iOS 7. Чи використовуєте ви цей код на iOS 6?
jaredsinclair

28

Я спробував усі відповіді вище, і мені нічого не вдалося. І ось що працює, якщо комусь це буде потрібно:

(Підкласи не потрібні)

// 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);

-Погляньте на результат.

Зверніть увагу на пробіл лівої кнопки та правої кнопки (права кнопка має поведінку за замовчуванням)

введіть тут опис зображення


10

Добре, я пішов в іншому "напрямку". Я зробив все належним чином за допомогою Storyboard з iOS 7 (припускаючи, що це буде працювати і надалі). А потім, використовуючи підхід опису підкласу, я підклас UIButtonз наступною реалізацією.

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

Отже, цей код працює, лише якщо пристрій встановлено до iOS 7.

Дякуємо за розуміння @jaredsinclair!


Працює нормально, хоча ви, ймовірно, хочете UIEdgeInsetsMake (0, 0, 0, 10) замість -10 для правої кнопки.
faafak Gezer

Я насправді стикався з проблемами з цим під час анімації на iOS 6, тому в підсумку витягнув це і пішов цим шляхом, stackoverflow.com/a/19169682/250190
Кріс Вагнер,

3

@jaredsinclair

Ось погляд на мій код.

// 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;

Бачите щось, що не так?

Ось результат. Друге зображення не настільки видно, тому що мені довелося встигнути зробити скріншот, і воно все ще переходить, але ви чітко бачите, як воно неправильно компенсується.

Хороший нормальний образ:

Це хороший образ, перш ніж орієнтуватися

Погане зміщення зображення:

Ви можете бачити, що зсув тепер знову вимкнено, перш ніж зафіксуватися на своєму місці

Приблизно через півтори секунди зображення повертається до вихідного місця.

Ось мій код для NavButton.h та .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

Мені все це виглядає добре. Можливо, обхідний шлях, який я запропонував вище, не працює в даному конкретному випадку. Ви можете спробувати дизайн за допомогою цієї кнопки вгорі праворуч, ні?
jaredsinclair

Угорі праворуч вже є своя кнопка! Мені просто доведеться подивитися, що ще я можу зробити. Будь-яка ідея, як дізнатися, коли кореневий контролер подання завантажується знову після того, як стек з’явився? Можливо, я можу просто додати кнопку до подання панелі навігації, а не як елемент кнопки панелі, а потім зникнути при натисканні контролера перегляду, а потім повернути її назад, коли з'явиться на контролері кореневого перегляду
Кайл Бегеман,

Додавання власних поглядів на панель навігації завдає шкоди. Я б не зробив цього, якби не мав іншого вибору. Ви можете спробувати скористатися методами UINavigationControllerDelegate, щоб дізнатись.
jaredsinclair

Рішення можна знайти тут: stackoverflow.com/questions/19233591/…
rockstarberlin


1

Не великий шанувальник підкласифікації UIButtonабо методу, що змінюється з відповіді Маріуса: https://stackoverflow.com/a/19317105/287403

Я просто використав просту обгортку і перемістив х рамки кнопки у негативному напрямку, поки не знайшов правильне розташування. Натискання кнопок, здається, теж добре (хоча ви можете збільшити ширину кнопки, щоб відповідати негативному зміщенню, якщо вам потрібно).

Ось код, який я використовую для створення нової кнопки повернення:

- (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

Я хотів би додати до @jaredsinclair підтвердженої відповіді наведені нижче методи заміщення для тих, хто має текст та / або зображення в навігаційній кнопці, інакше текст і зображення не будуть вирівняні (лише фонове зображення):

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

ps макрос:

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

0

* Рішення знайдено, читайте в кінці відповіді. *

@jaredsinclair

Я маю подібний випадок, як Кайл Бегеман

Це кнопка

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

і я використовую його в цьому випадку

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

Все виглядає чудово, за винятком того, що коли я повертаюся до попереднього ВК, кнопка переставляється сама. Як маленький стрибок. Для мене це стрибає одразу не через секунду. І це відбувається після того, як я піду робити popVC з NavigationViewController.

Редагувати: Відповідь нижче, метод приготування від Marius також допоміг мені.


0

Я придумав скорочену версію підходу jaredsinclair:

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

Працює як шарм.


0

в ios7 ви можете просто додати фіктивний барбутон для виправлення лівого простору, ви повинні додати фіктивний як перший, для правого, як останній

Наприклад, зліва, ви повинні додати це після встановлення оригінальних елементів або у viewdidload, якщо ви встановлюєте кнопки за допомогою розкадрування.

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

Це просто просто налаштувати imageEdgeInsets щоб виправити цю помилку.

спробуйте це.

    UIButton *actionBtn = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 40, 40)];
    [actionBtn setImage:[UIImage imageNamed:@"arrow.png"] forState:UIControlStateNormal];
    [actionBtn addTarget:self action:@selector(goToSetting) forControlEvents:UIControlEventTouchUpInside];
    actionBtn.imageEdgeInsets = UIEdgeInsetsMake(0, 9, 0, -9);

    UIBarButtonItem *profileBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:actionBtn];
    self.topViewController.navigationItem.rightBarButtonItems = @[profileBarButtonItem];

0

Перевірено в iOS 11

 public lazy var navigationBackBarButton : UIBarButtonItem = {

        let backBarButtonIcon = <Image Literal here>
        let backBarButton = UIBarButtonItem(image: backBarButtonIcon, style: .plain, target: self, action: #selector(self.backBarButtonTouched(_:)))
        backBarButton.imageInsets = UIEdgeInsets(top: 0.0, left: -9.0, bottom: 0.0, right: 0.0)
        return backBarButton

    }()

0

2018, iOS 11+, Swift 4.x, це працювало у мене.

Поєднання найкращих відповідей:

Власність

internal lazy var button_Favorite: UIButton = {
    let button = UIButton(type: .custom)
    button.setImage(.favNormal, for: .normal)
    button.contentHorizontalAlignment = .right
    button.imageEdgeInsets = UIEdgeInsetsMake(0, 0, 0, -9.0)
    return button
}()

В viewDidLoad:

    let barButton = UIBarButtonItem(customView: self.button_Favorite)
    self.button_Favorite.frame = CGRect(x: 0, y: 0, width: 40.0, height: 44.0)
    let negativeSpacer = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.fixedSpace, target: nil, action: nil)
    negativeSpacer.width = -10.0
    self.navigationItem.rightBarButtonItems = [negativeSpacer, barButton]

-1

Змініть наступне для вашого UIButton

Керування -> Вирівнювання -> Горизонтально, щоб вирівняти праворуч

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.