Подтвердить что ты не робот

Отрицательная проставка для UIBarButtonItem в навигационной панели на iOS 11

В iOS 10 и ниже был способ добавить отрицательный разделитель в массив кнопок в панели навигации, например:

UIBarButtonItem *negativeSpacer = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace target:nil action:nil];
negativeSpacer.width = -8;
self.navigationItem.leftBarButtonItems = @[negativeSpacer, [self backButtonItem]];

Это больше не работает на iOS 11 (проставка становится положительной, а не отрицательной). Я проверил иерархию представления элемента кнопки панели, и теперь он встроен в _UIButtonBarStackView. Как отрегулировать положение кнопки панели на iOS 11?

4b9b3361

Ответ 1

EDIT:

Это может больше не работать с iOS 13. Вы можете получить ошибку:

Ошибка клиента при попытке изменить поля макета частного представления

СТАРЫЙ ОТВЕТ:

Я нашел несколько хакерское решение на форумах разработчиков Apple: https://forums.developer.apple.com/thread/80075

Похоже, проблема возникает из-за того, что iOS 11 обрабатывает кнопки UIBarButtonItem .fixedSpace и как UINavigationBar выложен в iOS 11. В навигационных панелях теперь используется автоматическое расположение и поля макета для расположения кнопок. Решение, представленное в этом посте (внизу), состояло в том, чтобы установить все поля макета на желаемое значение.

class InsetButtonsNavigationBar: UINavigationBar {

    override func layoutSubviews() {
        super.layoutSubviews()

        for view in subviews {
            // Setting the layout margins to 0 lines the bar buttons items up at
            // the edges of the screen. You can set this to any number to change
            // the spacing.
            view.layoutMargins = .zero
        }
    }

}

Чтобы использовать эту новую навигационную панель с настраиваемым интервалом между кнопками, вам нужно будет обновить место создания любых контроллеров навигации с помощью следующего кода:

let navController = UINavigationController(navigationBarClass: InsetButtonsNavigationBar.self, 
                                                 toolbarClass: UIToolbar.self)
navController.viewControllers = [yourRootViewController]

Ответ 2

Просто обходной путь для моего случая, это может быть полезно для некоторых людей. Я хотел бы достичь этого:

введите описание изображения здесь и ранее я тоже использовал negativeSpacer. Теперь я понял это решение:

        let logoImage = UIImage(named: "your_image")
        let logoImageView = UIImageView(image: logoImage)
        logoImageView.frame = CGRect(x: -16, y: 0, width: 150, height: 44)
        logoImageView.contentMode = .scaleAspectFit
        let logoView = UIView(frame: CGRect(x: 0, y: 0, width: 10, height: 44))
        **logoView.clipsToBounds = false**
        logoView.addSubview(logoImageView)
        let logoItem = UIBarButtonItem(customView: logoView)
        navigationItem.leftBarButtonItem = logoItem

Ответ 3

Основываясь на ответе keithbhunter, я создал пользовательский UINavigationBar:

NavigationBarCustomMargins.h:

#import <UIKit/UIKit.h>

@interface NavigationBarCustomMargins : UINavigationBar

@property (nonatomic) IBInspectable CGFloat leftMargin;
@property (nonatomic) IBInspectable CGFloat rightMargin;

@end

NavigationBarCustomMargins.m:

#import "NavigationBarCustomMargins.h"

#define DefaultMargin 16
#define NegativeSpacerTag 87236223

@interface NavigationBarCustomMargins ()

@property (nonatomic) BOOL leftMarginIsSet;
@property (nonatomic) BOOL rightMarginIsSet;

@end

@implementation NavigationBarCustomMargins

@synthesize leftMargin = _leftMargin;
@synthesize rightMargin = _rightMargin;

- (void)layoutSubviews {
    [super layoutSubviews];

    if (([[[UIDevice currentDevice] systemVersion] compare:@"11.0" options:NSNumericSearch] != NSOrderedAscending)) {
        BOOL isRTL = [UIApplication sharedApplication].userInterfaceLayoutDirection == UIUserInterfaceLayoutDirectionRightToLeft;
        for (UIView *view in self.subviews) {
            view.layoutMargins = UIEdgeInsetsMake(0, isRTL ? self.rightMargin : self.leftMargin, 0, isRTL ? self.leftMargin : self.rightMargin);
        }
    } else {
        //left
        NSMutableArray *leftItems = [self.topItem.leftBarButtonItems mutableCopy];
        if (((UIBarButtonItem *)leftItems.firstObject).tag != NegativeSpacerTag) {

            UIBarButtonItem *negativeSpacer = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace target:nil action:nil];
            negativeSpacer.tag = NegativeSpacerTag;
            negativeSpacer.width = self.leftMargin - DefaultMargin;
            [leftItems insertObject:negativeSpacer atIndex:0];

            [self.topItem setLeftBarButtonItems:[leftItems copy] animated:NO];
        }

        //right
        NSMutableArray *rightItems = [self.topItem.rightBarButtonItems mutableCopy];
        if (((UIBarButtonItem *)rightItems.firstObject).tag != NegativeSpacerTag) {

            UIBarButtonItem *negativeSpacer = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace target:nil action:nil];
            negativeSpacer.tag = NegativeSpacerTag;
            negativeSpacer.width = self.rightMargin - DefaultMargin;
            [rightItems insertObject:negativeSpacer atIndex:0];
            [self.topItem setRightBarButtonItems:[rightItems copy] animated:NO];
        }
    }
}

- (CGFloat)leftMargin {
    if (_leftMarginIsSet) {
        return _leftMargin;
    }
    return DefaultMargin;
}

- (CGFloat)rightMargin {
    if (_rightMarginIsSet) {
        return _rightMargin;
    }
    return DefaultMargin;
}

- (void)setLeftMargin:(CGFloat)leftMargin {
    _leftMargin = leftMargin;
    _leftMarginIsSet = YES;
}

- (void)setRightMargin:(CGFloat)rightMargin {
    _rightMargin = rightMargin;
    _rightMarginIsSet = YES;
}

@end

После этого я устанавливаю собственный класс в свой UINavigationController в Interface Builder и просто устанавливаю необходимые поля: Снимок экрана 1

Прекрасно работает. Поддерживает RTL и iOS до 11: Снимок экрана 2

Ответ 4

Для меня этот ответ поможет fooobar.com/info/168156/...
В частности, я установил как imageEdgeInsets, так и titleEdgeInsets, потому что у моей кнопки есть изображение и название вместе