Пользовательский заголовок кнопки «Назад» и сохранение жеста прокрутки назад

Проблема: я хотел бы настроить заголовок кнопки возврата назад во всплывающем контроллере, например Whatsapp (‹ Чаты (2) / ‹ Чаты (3)).

Однако для назначения нового backBarButtonItem в контроллере всплывающего представления будет отключен жест прокрутки назад, если вы используете

self.navigationController.interactivePopGestureRecognizer.delegate = self;

чтобы жест продолжал работать, это доставит вам больше проблем (слишком много ошибок).


person Rex Lam    schedule 20.05.2014    source источник
comment
Обратитесь к этому stackoverflow.com/a/9871741/1707115.   -  person Kaiusee    schedule 20.05.2014
comment
Я написал быстрое расширение, поэтому вы можете просто добавить этот файл и использовать UINavigationController по умолчанию, и он должен работать. gist.github.com/donpironet/f5ce9857f422df46d52f053c22d96c8c   -  person user1007522    schedule 29.06.2016


Ответы (4)


Вы должны установить свойство self.navigationItem.backBarButtonItem для ViewController, которое появляется до того, как будет отображаться заголовок.

В примере с Whatsapp вам нужно будет установить заголовок в контроллере представления списка чатов.

Что-то такое:

self.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"chats(2)" style:UIBarButtonItemStylePlain target:nil action:nil];

После этого вы можете установить только title из self.navigationItem.backBarButtonItem.

person Gonzo    schedule 20.05.2014
comment
Я пробовал это раньше, self.navigationItem.backBarButtonItem — это нулевой объект. - person Rex Lam; 20.05.2014
comment
Да, ты был прав. Я отредактировал ответ, теперь должно работать! - person Gonzo; 20.05.2014
comment
Нет... это не сработает, если вы назначите backBarButtonItem в первом контроллере представления, после нажатия на второй контроллер представления backBarButtonItem снова станет нулевым, даже если вы назначите его в методе prepareForSegue или так же, как мой ответ в пользовательском контроллере навигации . Вот почему я сделал это общественным достоянием. - person Rex Lam; 20.05.2014
comment
Просто чтобы быть уверенным, вы устанавливаете кнопку на первом контроллере просмотра. Но вы устанавливаете кнопку на self, а не на segue.destinationViewController, верно? - person Gonzo; 20.05.2014
comment
viewController.navigationItem.backBarButtonItem = backButton; Это должен быть целевой контроллер представления. - person Rex Lam; 20.05.2014
comment
На первый взгляд может показаться нелогичным, но этот viewController должен быть самим собой firstViewController. - person Gonzo; 20.05.2014
comment
Только что проверил, это контроллер представления назначения. Да, это выглядит нелогично, я тоже не понимаю такого поведения - person Rex Lam; 20.05.2014

Если вы хотите очистить заголовок кнопки «Назад» во всем приложении, одним из решений является прокрутка viewDidLoad и пустого заголовка кнопки «Назад» в прокручиваемом viewDidLoad. И это не повлияет на работу InteractivePopGestureRecognizer (убедитесь, что InteractivePopGestureRecognizer включен).

            @implementation UIViewController (Customizations)

            + (void)load {
                static dispatch_once_t onceToken;
                dispatch_once(&onceToken, ^{
                    [UIViewController swizzleClass:[UIViewController class] method:@"viewDidLoad"];
                });
            }

            + (void)swizzleClass:(Class)class method:(NSString*)methodName {
                SEL originalMethod = NSSelectorFromString(methodName);
                SEL newMethod = NSSelectorFromString([NSString stringWithFormat:@"%@%@", @"override_", methodName]);
                [self swizzle:class from:originalMethod to:newMethod];
            }

            + (void)swizzle:(Class)class from:(SEL)original to:(SEL)new {
                Method originalMethod = class_getInstanceMethod(class, original);
                Method newMethod = class_getInstanceMethod(class, new);
                if(class_addMethod(class, original, method_getImplementation(newMethod), method_getTypeEncoding(newMethod))) {
                    class_replaceMethod(class, new, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
                } else {
                    method_exchangeImplementations(originalMethod, newMethod);
                }
            }

            - (void)override_viewDidLoad {
                //Empty back button title
                UIBarButtonItem *backButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"" style:UIBarButtonItemStylePlain target:nil action:nil];
                [self.navigationItem setBackBarButtonItem:backButtonItem];
                [self override_viewDidLoad];
            }

            @end
person Narek Safaryan    schedule 27.01.2015
comment
Спасибо, вы спасли мой день - person xxi; 01.10.2018

Для людей, которые просто хотят использовать UINavigationController и исправить это. Я написал расширение для UINavigationController в Swift.

У него нет проблемы, связанной с тем, что стек может быть сломан, если вы проведете пальцем назад в ближайшее время.

extension UINavigationController: UINavigationControllerDelegate, UIGestureRecognizerDelegate {

    public override static func initialize() {
        struct Static {
            static var token: dispatch_once_t = 0
        }

        if self !== UINavigationController.self {
            return
        }

        dispatch_once(&Static.token) {

            // Swizzle viewDidLoad

            self.swizzleViewDidLoad()

            // Swizzle pushViewController

            self.swizzlePushController()
        }
    }

    // MARK: - Helpers

    static func swizzleViewDidLoad() {
        let originalViewDidLoadSelector = #selector(UINavigationController.viewDidLoad)
        let swizzledViewDidLoadSelector = #selector(UINavigationController.newViewDidLoad)

        let originalViewDidLoadMethod = class_getInstanceMethod(self, originalViewDidLoadSelector)
        let swizzledViewDidLoadMethod = class_getInstanceMethod(self, swizzledViewDidLoadSelector)

        let didAddViewDidLoadMethod = class_addMethod(self, originalViewDidLoadSelector, method_getImplementation(swizzledViewDidLoadMethod), method_getTypeEncoding(swizzledViewDidLoadMethod))

        if didAddViewDidLoadMethod {
            class_replaceMethod(self, swizzledViewDidLoadSelector, method_getImplementation(originalViewDidLoadMethod), method_getTypeEncoding(swizzledViewDidLoadMethod))
        } else {
            method_exchangeImplementations(originalViewDidLoadMethod, swizzledViewDidLoadMethod);
        }
    }

    static func swizzlePushController() {
        let originalPushControllerSelector = #selector(UINavigationController.pushViewController(_:animated:))
        let swizzledPushControllerSelector = #selector(UINavigationController.newPushViewController(_:animated:))

        let originalPushControllerMethod = class_getInstanceMethod(self, originalPushControllerSelector)
        let swizzledPushControllerMethod = class_getInstanceMethod(self, swizzledPushControllerSelector)

        let didAddPushControllerMethod = class_addMethod(self, originalPushControllerSelector, method_getImplementation(swizzledPushControllerMethod), method_getTypeEncoding(swizzledPushControllerMethod))

        if didAddPushControllerMethod {
            class_replaceMethod(self, swizzledPushControllerSelector, method_getImplementation(originalPushControllerMethod), method_getTypeEncoding(swizzledPushControllerMethod))
        } else {
            method_exchangeImplementations(originalPushControllerMethod, swizzledPushControllerMethod);
        }
    }

    // MARK: - Method Swizzling

    func newViewDidLoad() {
        self.newViewDidLoad()

        self.interactivePopGestureRecognizer?.delegate = self
        self.delegate = self
    }

    func newPushViewController(viewController: UIViewController, animated: Bool) {
        self.interactivePopGestureRecognizer?.enabled = false

        self.newPushViewController(viewController, animated: animated)
    }

    // MARK: - UINavigationControllerDelegate

    public func navigationController(navigationController: UINavigationController, didShowViewController viewController: UIViewController, animated: Bool) {
        self.interactivePopGestureRecognizer?.enabled = true
    }
}
person user1007522    schedule 29.06.2016

Отвечать:

Потратив день на эту проблему, у меня есть довольно простое и легкое решение, которое работает как на iOS 6, так и на iOS 7:

1). Пользовательский стиль (цвет, шрифт) в AppDelegate (предполагается, что вы будете использовать один и тот же стиль для всех контроллеров)

2). Создайте собственный UINavigationController следующим образом:

Кустомбакнавигатионконтроллер.h

@interface CustomBackNavigationController : UINavigationController <UINavigationControllerDelegate>

@property (nonatomic, strong) UIBarButtonItem *backButton;

@end

CustomBackNavigationController.m

@implementation CustomBackNavigationController

@synthesize backButton;

- (void)viewDidLoad
{
    [super viewDidLoad];

    self.delegate = self;
}


- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated
{
    backButton = [[UIBarButtonItem alloc] initWithTitle:@"Chats" style:UIBarButtonItemStyleBordered target:nil action:nil];
    viewController.navigationItem.backBarButtonItem = backButton;
}

@end

в контроллерах всплывающего окна просто измените заголовок backButton следующим образом

- (void)someMethod
{
    CustomBackNavigationController *customBackNavigationController = (CustomBackNavigationController *) self.navigationController;

    [customBackNavigationController.backButton setTitle:@"Chats (1)"];
}

Вот и все !

person Rex Lam    schedule 20.05.2014