Как создать backBarButtomItem с пользовательским представлением для UINavigationController - PullRequest
34 голосов
/ 09 февраля 2009

У меня есть UINavigationController, в который я выдвигаю несколько просмотров. Внутри viewDidLoad для одного из этих представлений я хочу установить self.navigationItem.backBarButtonItem в настраиваемое представление (на основе настраиваемого изображения). Я не знаю почему, но это не похоже на работу. Вместо этого я получаю стандартную кнопку «назад».

UIButton *backButton = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 63, 30)];
[backButton setImage:[UIImage imageNamed:@"back_OFF.png"] forState:UIControlStateNormal];
[backButton setImage:[UIImage imageNamed:@"back_ON.png"] forState:UIControlStateSelected];
UIBarButtonItem *backButtonItem = [[UIBarButtonItem alloc] initWithCustomView:backButton];
self.navigationItem.backBarButtonItem = backButtonItem;
[backButtonItem release];
[backButton release];

Я тестировал со стандартным названием, и это сработало. Что не так с приведенным выше кодом?

self.navigationItem.backBarButtonItem = [[[UIBarButtonItem alloc] initWithTitle:@"Prout" style:UIBarButtonItemStyleDone target:nil action:nil] autorelease];

Спасибо за любую помощь в этом.

Ответы [ 14 ]

27 голосов
/ 22 марта 2012

Начиная с iOS5, у нас есть отличный новый способ настройки внешнего вида практически любого элемента управления с использованием протокола UIAppearance , т.е. [UIBarButtonItem appearance]. Внешний прокси-сервер позволяет создавать в приложении изменения во внешнем виде элементов управления. Ниже приведен пример пользовательской кнопки «Назад», созданной с помощью прокси-сервера внешнего вида.

enter image description here

Используйте приведенный ниже пример кода, чтобы создать кнопку возврата с пользовательскими изображениями для нормального и выделенного состояний. Вызовите следующий метод из приложения appDelegate application:didFinishLaunchingWithOptions:

- (void) customizeAppearance {

UIImage *i1 = [[UIImage imageNamed:@"custom_backButton_30px"]
                      resizableImageWithCapInsets:UIEdgeInsetsMake(0, 15, 0, 6)];
UIImage *i2 = [[UIImage imageNamed:@"custom_backButton_24px"] 
                      resizableImageWithCapInsets:UIEdgeInsetsMake(0, 15, 0, 6)];

[[UIBarButtonItem appearance] setBackButtonBackgroundImage:i1 
                              forState:UIControlStateNormal 
                              barMetrics:UIBarMetricsDefault];

[[UIBarButtonItem appearance] setBackButtonBackgroundImage:i2 
                              forState:UIControlStateNormal 
                              barMetrics:UIBarMetricsLandscapePhone];

[[UIBarButtonItem appearance] setBackButtonBackgroundImage:i1
                              forState:UIControlStateHighlighted 
                              barMetrics:UIBarMetricsDefault];

[[UIBarButtonItem appearance] setBackButtonBackgroundImage:i2 
                              forState:UIControlStateHighlighted 
                              barMetrics:UIBarMetricsLandscapePhone];
}

Это всего лишь быстрый пример. Обычно вы хотите иметь отдельные изображения для нормального и выделенного (нажатого) состояния.

Если вы заинтересованы в настройке внешнего вида других элементов управления, некоторые хорошие примеры можно найти здесь: http://ios.biomsoft.com/2011/10/13/user-interface-customization-in-ios-5/

19 голосов
/ 09 февраля 2009

Я вполне уверен, что backBarButtonItem это свойство только для чтения. Вместо изменения backBarButtonItem, попробуйте установить пользовательский leftBarButtonItem и скрыть backBarButtonItem:

self.navigationItem.leftBarButtonItem = [[[UIBarButtonItem alloc] initWithTitle:@"Prout" style:UIBarButtonItemStyleDone target:nil action:nil] autorelease];
self.navigationItem.hidesBackButton = YES;

Вам также необходимо убедиться, что вы подключили пользовательскую кнопку для вызова обратного действия на UINavigationBar.

15 голосов
/ 14 октября 2010

Мне никогда не удавалось создать правильный UIBarButtonItem с пользовательским представлением и setBackBarButtonItem.

Вот решение, которое я нашел: пусть net UINavigationControllerDelegate обрабатывает все! Хитрость заключается в том, чтобы вызвать метод popViewControllerAnimated: viewController.navigationController, чтобы вам не нужно было создавать какой-либо пользовательский метод.

- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated {
    if([navigationController.viewControllers count ] > 1) {
        UIView *backButtonView = [[UIView alloc] initWithFrame:CGRectMake(0,0,70,35)];
        UIButton *myBackButton = [[UIButton buttonWithType:UIButtonTypeCustom] retain];
        [myBackButton setFrame:CGRectMake(0,0,70,35)];
        [myBackButton setImage:[UIImage imageNamed:@"back.png"] forState:UIControlStateNormal];
        [myBackButton setEnabled:YES];
        [myBackButton addTarget:viewController.navigationController action:@selector(popViewControllerAnimated:) forControlEvents:UIControlEventTouchUpInside];
        [backButtonView addSubview:myBackButton];
        [myBackButton release];
        UIBarButtonItem* backButton = [[UIBarButtonItem alloc] initWithCustomView:backButtonView];
        viewController.navigationItem.leftBarButtonItem = backButton;
        [backButtonView release];
        [backButton release];
    }
}
10 голосов
/ 08 декабря 2011

Если ваша конечная цель - просто заменить изображение, используемое для кнопки «Назад», вы можете использовать новый метод на UIBarButtonItem, доступный в iOS 5.0:

setBackButtonBackgroundImage: Форстат: barMetrics:

Apple Docs: http://developer.apple.com/library/ios/#documentation/UIKit/Reference/UIBarButtonItem_Class/Reference/Reference.html

Вот простой пример, который устанавливает пользовательское фоновое изображение для всех кнопок «назад» в вашем приложении:

UIImage *toolbarBackButtonBackgroundPortrait = [[UIImage imageNamed:@"toolbarBackButtonPortrait"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 17, 0, 6)];
UIImage *toolbarBackButtonBackgroundLandscape = [[UIImage imageNamed:@"toolbarBackButtonLandscape"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 17, 0, 6)];

[[UIBarButtonItem appearance] setBackButtonBackgroundImage:toolbarBackButtonBackgroundPortrait forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
[[UIBarButtonItem appearance] setBackButtonBackgroundImage:toolbarBackButtonBackgroundLandscape forState:UIControlStateNormal barMetrics:UIBarMetricsLandscapePhone];
6 голосов
/ 07 июня 2009

Я был бы готов поспорить, что это ошибка со стороны Apple, поскольку я сталкиваюсь с точно такой же проблемой. Причина в том, что я могу заставить пользовательский UIBarButtonItem появляться, но только когда я не пытаюсь использовать метод initWithCustomView:. В соответствии с API контроллер навигации проверяет следующие вещи при нажатии на новый контроллер представления:

  1. Если в новом контроллере представления верхнего уровня есть элемент пользовательской кнопки левой панели, этот элемент отображается. Чтобы указать пользовательский элемент кнопки левой панели, установите свойство leftBarButtonItem элемента навигации контроллера представления.
  2. Если контроллер представления верхнего уровня не имеет пользовательского элемента кнопки левой панели, но элемент навигации предыдущего контроллера представления имеет действительный элемент в свойстве backBarButtonItem, панель навигации отображает этот элемент.
  3. Если элемент пользовательской кнопки панели не указан ни одним из контроллеров представления, используется кнопка возврата по умолчанию, а ее заголовок устанавливается равным значению свойства заголовка предыдущего контроллера представления, то есть одного контроллера представления уровень вниз в стеке. (Если в стеке навигации только один контроллер представления, кнопка возврата не отображается.)

Мой случай (как и ваш) равен 2. Я указываю код точно такой же, как ваш (т. Е. Создаю UIButton, устанавливаю его свойства image для различных состояний, создаю UIBarButtonItem, инициализируя его с UIButton, затем устанавливая свойство backBarButtonItem моего текущего контроллера вида в UIBarButtonItem); однако, когда я позже нажимаю на свой контроллер вида, на левой стороне моего контроллера навигации вообще ничего не отображается. Странно, я могу щелкнуть, где должна быть кнопка «Назад», и на ней появляется контроллер представления.

Интересно, что если я создаю UIBarButtonItem, используя метод initWithTitle:style:target:action: вместо initWithCustomView:, он показывает пользовательскую кнопку с пользовательским заголовком. Кроме того, как упомянул Трэвис, использование свойства leftBarButtonItem работает просто отлично. Я бы предпочел придерживаться санкционированной логики, однако, указав кнопку «Назад» для текущего контроллера представления - которая будет отображаться позже при нажатии на новый контроллер представления - вместо создания левой кнопки для следующего контроллера представления что, возможно, не должно волновать что-либо, относящееся к контроллеру представления, который был до него. : - \

5 голосов
/ 25 августа 2011

Установите backBarButtonItem до , нажимая viewController с навигационным контроллером. Установка backBarButtonItem в viewDidLoad не работает.

Скажем, у меня есть MyTableViewController. Когда пользователь нажимает на конкретную строку, я хочу нажать AnotherViewController с помощью navigationController. Код выглядит примерно так:

// in MyTableViewController's tableView:didSelectRowAtIndexPath method...
UIBarButtonItem *backButton = [[UIBarButtonItem alloc] 
                               initWithImage:[UIImage imageNamed:@"yourImage.png"]
                                       style:UIBarButtonItemStyleBordered 
                                      target:nil 
                                      action:nil];

self.navigationItem.backBarButtonItem = backButton;
[backButton release];

[self.navigationController pushViewController:anotherViewController];

При отображении другого ViewController кнопка возврата на панели навигации будет иметь @"yourImage.png" и стиль кнопки возврата по умолчанию (прямоугольная стрелка). Также обратите внимание, что можно передать nil как target. Кнопка ведет себя как кнопка возврата.

2 голосов
/ 12 октября 2010

У меня тоже были проблемы с customView на navigationItem.backBarButtonItem. Я подозреваю, что это, вероятно, просто b0rked в SDK.

Хотя обходные пути, описанные здесь, работают, я нашел решение, которое немного более элегантно: оно все еще использует navigationItem.leftBarButtonItem, но позаботится о нем автоматически (больше не нужно «детское» представление) контроллеры должны знать что-либо об их «родителе» и / или вручную устанавливать navigationItem.leftBarButtonItem).


Прежде всего, пусть у какого-нибудь класса будет UINavigationControllerDelegate для UINavigationController, кнопка возврата которого вас интересует. Затем, в этом классе, установите что-то вроде следующего willShowViewController метода делегата:

-(void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated{
    // reference to view controller stack
    NSArray *viewControllers = [ navigationController viewControllers ];
    if( [ viewControllers count ] > 1 ){
        // the view controller we'll be linking to
        UIViewController *backViewController = [ viewControllers objectAtIndex: [ viewControllers count ] - 2 ];
        // create custom UIBarButtonItem
        UIBarButtonItem *leftButton = [[ UIBarButtonItem alloc ] initWithCustomView: someCustomView ];
        // set it as the leftBarButtonItem on the incoming viewcontroller
        viewController.navigationItem.leftBarButtonItem = leftButton;
        // tidy up
        [ leftButton release ];
    }
}

У меня были некоторые дальнейшие проблемы с этим; кажется, что UIBarButtonItem.action и UIBarButtonItem.target не работают, когда это navigationItem.leftBarButtonItem. Итак, у вас есть пользовательская кнопка «назад», которая на самом деле не возвращается. Я оставлю реагирование на прикосновения в вашем пользовательском представлении в качестве упражнения для читателя (я использовал UIButton), но вам нужно добавить этот метод в ваш класс делегатов:

-(void)onDummyBackButtonTapped{
    [ someNavigationController popViewControllerAnimated: YES ];
}

и включите его, когда ваш пользовательский вид коснется.

2 голосов
/ 18 августа 2010

Даже если на него уже ответили, это сработало для меня:

UIBarButtonItem *backButton = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"backArrow.png"] style:UIBarButtonItemStyleBordered target:nil action:nil];
self.navigationItem.backBarButtonItem = backButton;
[backButton release];

Кстати: даже в iOS4 инициализация моей кнопки возврата с помощью initWithCustomView: у меня не работала. ; (

1 голос
/ 19 апреля 2013

Так я создаю пользовательскую квадратную кнопку назад со стрелкой вместо обычного текста.

Я просто настраиваю делегата для своего UINavigationController. Я использую делегат приложения для этого, потому что контроллер корневого представления окна - UINavigationController, которым я хочу управлять.

Итак, AppDelegate.m (ARC):

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    ((UINavigationController*)_window.rootViewController).delegate = self;
    return YES;
}

#pragma mark - UINavigationControllerDelegate

- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated {
    if(navigationController.viewControllers.count > 1) {
        UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
        [button setImage:[UIImage imageNamed:@"back-arrow.png"] forState:UIControlStateNormal];
        [button setBackgroundColor:[UIColor grayColor]];
        button.frame = CGRectMake(0, 0, 44, 44);
        viewController.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:button];
        [button addEventHandler:^(id sender) {
            [navigationController popViewControllerAnimated:YES];
        } forControlEvents:UIControlEventTouchUpInside];
    }
}

Я использую BlocksKit , чтобы перехватить событие нажатия кнопки. Это очень удобно для подобных вещей, но вы также можете использовать обычный addTarget: action: forControlEvents: method

1 голос
/ 28 октября 2010

backBarButtonItem навигационного контроллера установлен на элементе, на заголовок которого вы пытаетесь повлиять.

т.е. в контроллере вида Page 1, скажем, viewdidLoad:

self.title = @"Page 1 of 4";

self.navigationItem.backBarButtonItem =
[[[UIBarButtonItem alloc] initWithTitle:@"Page 1"
                                  style:UIBarButtonItemStyleBordered
                                 target:nil
                                 action:nil] autorelease];

Вы не переопределите это на странице 2.

Документация для UINavigationItem: backBarButtonItem проясняет это:

Когда этот предмет является задним панель навигации - когда это следующий пункт ниже верхнего элемента - это может быть представлен в виде кнопки назад на Панель навигации. Используйте это свойство для указать кнопку назад. Цель и действие элемента кнопки задней панели Вы должны установить ноль. По умолчанию значение - отображение элемента панели кнопок название элемента навигации.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...