UIBarButtonItem с пользовательским представлением неправильно выровнен на iOS 7 при использовании в качестве элементов левой или правой панели навигации
Следующий код работает через 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 кнопка кажется смещенной вправо или влево на слишком много пикселей:
Как правильно настроить элементы панели кнопок?
15 ответов
Чтобы исправить эту ошибку, вы должны создать подкласс 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.
Работает до 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];
Я перепробовал все ответы выше, и у меня ничего не получалось. И вот что работает, если кому-то это понадобится:
(Нет необходимости в подклассах)
// 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);
-Посмотрите на результат.
Обратите внимание на место для левой кнопки и правой кнопки (правая кнопка имеет поведение по умолчанию)
Хорошо, я пошел в другом направлении. Я выстроил все правильно через 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!
@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
Лучшее решение без прыгающих кнопок arround можно найти здесь:
Пользовательское выравнивание UIBarButtonItem отключено с iOS7
Я хотел бы добавить к подтвержденному ответу @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];
}
пс макрос это:
#define SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedAscending)
Не большой поклонник подклассов UIButton
или метод, извилистый из ответа Мариуса: /questions/22625433/polzovatelskoe-vyiravnivanie-uibarbuttonitem-otklyucheno-s-ios7/22625459#22625459
Я просто использовал простую обертку и перемещал x рамки кнопки в отрицательном направлении, пока не нашел правильное положение. Нажатие кнопки также выглядит вполне приемлемым (хотя вы можете увеличить ширину кнопки, чтобы она соответствовала отрицательному смещению, если вам нужно).
Вот код, который я использую для создания новой кнопки возврата:
- (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];
}
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]
* Решение найдено, прочитайте в конце ответа. *
@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];
Все выглядит отлично, за исключением того, что, когда я возвращаюсь к предыдущему VC, кнопка перемещается сама. Как маленький прыжок. Для меня это прыгает сразу же, не через секунду. И это происходит после того, как я делаю popVC из NavigationViewController.
Редактировать: ответ ниже, метод мерцания от Мариуса также помог мне.
В ios7 вы можете просто добавить пустышку barbuttonitem для исправления левого пространства, вы должны добавить пустышку первым, а правым последним
Например, слева, вы должны добавить это после установки исходных элементов или в 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];
Это просто настроить 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];
Я придумал сокращенную версию подхода Джаредснклера:
- (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);
}
Работает как шарм.
Протестировано в 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
}()
Измените следующее для вашего UIButton
Управление -> Выравнивание -> По горизонтали выровнять по правому краю