Изменять кнопки в iOS 7 отключить свайп для перехода обратно


У меня есть приложение iOS 7, где я устанавливаю пользовательскую кнопку Назад следующим образом:

    UIImage *backButtonImage = [UIImage imageNamed:@"back-button"];
    UIButton *backButton = [UIButton buttonWithType:UIButtonTypeCustom];

    [backButton setImage:backButtonImage forState:UIControlStateNormal];
    backButton.frame = CGRectMake(0, 0, 20, 20);

    [backButton addTarget:self
                   action:@selector(popViewController)
         forControlEvents:UIControlEventTouchUpInside];

    UIBarButtonItem *backBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:backButton];
    viewController.navigationItem.leftBarButtonItem = backBarButtonItem;

но это отключает iOS 7 "проведите пальцем слева направо" жест для перехода к предыдущему контроллеру. Кто-нибудь знает, как я могу создать пользовательскую кнопку и сохранить этот жест включен?

изменить: Я попытался установить viewController.navigationItem.backBarButtonItem вместо этого, но это, кажется, не показывает мой пользовательский образ.

13 77

13 ответов:

важно: Это хак. Я бы рекомендовал взглянуть на это ответ.

вызов следующей строки после назначения leftBarButtonItem работала на меня:

self.navigationController.interactivePopGestureRecognizer.delegate = self;

Edit: Это не работает, если вызывается в init методы. Он должен быть вызван в viewDidLoad или аналогичные методы.

используйте свойства backIndicatorImage и backIndicatorTransitionMaskImage панели UINavigationBar, если это вообще возможно. Установка их на UIAppearanceProxy может легко изменить поведение в вашем приложении. Морщина заключается в том, что вы можете установить их только на ios 7, но это работает, потому что вы можете использовать только поп-жест на ios 7 в любом случае. Ваш обычный стиль ios 6 может остаться нетронутым.

UINavigationBar* appearanceNavigationBar = [UINavigationBar appearance];
//the appearanceProxy returns NO, so ask the class directly
if ([[UINavigationBar class] instancesRespondToSelector:@selector(setBackIndicatorImage:)])
{
    appearanceNavigationBar.backIndicatorImage = [UIImage imageNamed:@"back"];
    appearanceNavigationBar.backIndicatorTransitionMaskImage = [UIImage imageNamed:@"back"];
    //sets back button color
    appearanceNavigationBar.tintColor = [UIColor whiteColor];
}else{
    //do ios 6 customization
}

попытка манипулировать делегатом interactivePopGestureRecognizer будет это приводит к множеству проблем.

Я видел это решение http://keighl.com/post/ios7-interactive-pop-gesture-custom-back-button/ Какие подклассы UINavigationController. Это лучшее решение, поскольку оно обрабатывает случай, когда вы проводите пальцем до того, как контроллер находится на месте, что вызывает сбой.

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

Так код подкласс UINavigationController должен выглядеть так:

@implementation NavigationController

- (void)viewDidLoad {
    [super viewDidLoad];
    __weak NavigationController *weakSelf = self;

    if ([self respondsToSelector:@selector(interactivePopGestureRecognizer)]) {
        self.interactivePopGestureRecognizer.delegate = weakSelf;
        self.delegate = weakSelf;
    }
}

- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated {
    // Hijack the push method to disable the gesture
    if ([self respondsToSelector:@selector(interactivePopGestureRecognizer)]) {
        self.interactivePopGestureRecognizer.enabled = NO;
    }
    [super pushViewController:viewController animated:animated];
}

#pragma mark - UINavigationControllerDelegate

- (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animate {
    // Enable the gesture again once the new controller is shown
    self.interactivePopGestureRecognizer.enabled = ([self respondsToSelector:@selector(interactivePopGestureRecognizer)] && [self.viewControllers count] > 1);
}

@end

Я использую

[[UINavigationBar appearance] setBackIndicatorImage:[UIImage imageNamed:@"nav_back.png"]];
[[UINavigationBar appearance] setBackIndicatorTransitionMaskImage:[UIImage imageNamed:@"nav_back.png"]];

[UIBarButtonItem.appearance setBackButtonTitlePositionAdjustment:UIOffsetMake(0, -64) forBarMetrics:UIBarMetricsDefault];

Я также скрываю кнопку Назад, заменяя ее пользовательским левым баритемом.
Удаление делегата interactivePopGestureRecognizer после действия push сработало для меня:

[self.navigationController pushViewController:vcToPush animated:YES];

// Enabling iOS 7 screen-edge-pan-gesture for pop action
if ([self.navigationController respondsToSelector:@selector(interactivePopGestureRecognizer)]) {
    self.navigationController.interactivePopGestureRecognizer.delegate = nil;
}
navigationController.interactivePopGestureRecognizer.delegate = (id<UIGestureRecognizerDelegate>)self;

это от http://stuartkhall.com/posts/ios-7-development-tips-tricks-hacks, но это вызывает несколько ошибок:

  1. нажмите другой viewController в navigationController при прокрутке с левого края экрана;
  2. или проведите пальцем от левого края экрана, когда topViewController выскакивает из navigationController;

например, когда rootViewController navigationController является показывая, проведите пальцем от левого края экрана и нажмите что-то (быстро), чтобы нажать anotherViewController в navigationController, затем

  • rootViewController не отвечает на любое событие касания;
  • anotherViewController не будет отображаться;
  • проведите пальцем от края экрана снова, anotherViewController будет показан;
  • Нажмите кнопку "назад", чтобы открыть anotherViewController, аварии!

так что вы должны реализовать UIGestureRecognizerDelegate метод self.navigationController.interactivePopGestureRecognizer.delegate такой:

- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer {
    if (gestureRecognizer == navigationController.interactivePopGestureRecognizer) {
        return !navigationController.<#TODO: isPushAnimating#> && [navigationController.viewControllers count] > 1;
    }
    return YES;
}

вот swift3 версия ответ Ника H247

class NavigationController: UINavigationController {
  override func viewDidLoad() {
    super.viewDidLoad()
    if responds(to: #selector(getter: interactivePopGestureRecognizer)) {
      interactivePopGestureRecognizer?.delegate = self
      delegate = self
    }
  }

  override func pushViewController(_ viewController: UIViewController, animated: Bool) {
    if responds(to: #selector(getter: interactivePopGestureRecognizer)) {
      interactivePopGestureRecognizer?.isEnabled = false
    }
    super.pushViewController(viewController, animated: animated)
  }
}

extension NavigationController: UINavigationControllerDelegate {
  func navigationController(_ navigationController: UINavigationController, didShow viewController: UIViewController, animated: Bool) {
    interactivePopGestureRecognizer?.isEnabled = (responds(to: #selector(getter: interactivePopGestureRecognizer)) && viewControllers.count > 1)
  }
}

extension NavigationController: UIGestureRecognizerDelegate {}

попробовать self.navigationController.interactivePopGestureRecognizer.enabled = YES;

Я этого не писал, но следующий блог очень помог и решил мои проблемы с пользовательской кнопкой навигации:

http://keighl.com/post/ios7-interactive-pop-gesture-custom-back-button/

таким образом, он реализует пользовательский UINavigationController, который использует делегат Pop gesture. Очень чистый и портативный!

код:

@interface CBNavigationController : UINavigationController <UINavigationControllerDelegate, UIGestureRecognizerDelegate>
@end

@implementation CBNavigationController

- (void)viewDidLoad
{
  __weak CBNavigationController *weakSelf = self;

  if ([self respondsToSelector:@selector(interactivePopGestureRecognizer)])
  {
    self.interactivePopGestureRecognizer.delegate = weakSelf;
    self.delegate = weakSelf;
  }
}

// Hijack the push method to disable the gesture

- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated
{
  if ([self respondsToSelector:@selector(interactivePopGestureRecognizer)])
    self.interactivePopGestureRecognizer.enabled = NO;

  [super pushViewController:viewController animated:animated];
}

#pragma mark UINavigationControllerDelegate

- (void)navigationController:(UINavigationController *)navigationController
       didShowViewController:(UIViewController *)viewController
                    animated:(BOOL)animate
{
  // Enable the gesture again once the new controller is shown

  if ([self respondsToSelector:@selector(interactivePopGestureRecognizer)])
    self.interactivePopGestureRecognizer.enabled = YES;
}

правка. Добавлено исправление проблем, когда пользователь пытается прокрутить влево на корневом представлении контроллер:

- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer {

    if ([self respondsToSelector:@selector(interactivePopGestureRecognizer)] &&
        self.topViewController == [self.viewControllers firstObject] &&
        gestureRecognizer == self.interactivePopGestureRecognizer) {

        return NO;
    }

    return YES;
}

RootView

override func viewDidAppear(_ animated: Bool) {
    self.navigationController?.interactivePopGestureRecognizer?.isEnabled = false
}

ChildView

override func viewDidLoad() {
    self.navigationController?.interactivePopGestureRecognizer?.isEnabled = true
    self.navigationController?.interactivePopGestureRecognizer?.delegate = self
} 

extension ChildViewController: UIGestureRecognizerDelegate {}

у меня была аналогичная проблема, когда я назначал текущий контроллер представления в качестве делегата для интерактивного поп-жеста, но нарушил бы жест на любые представления, которые были нажаты, или представления под представлением в стеке навигации. Способ, которым я решил это, состоял в том, чтобы установить делегат в -viewDidAppear, затем установите его в ноль в -viewWillDisappear. Это позволило моим другим взглядам работать правильно.

представьте, что мы используем шаблон проекта Apple по умолчанию master/detail, где master-это контроллер табличного представления, и нажатие на него покажет контроллер подробного представления.

мы хотим настроить кнопку Назад, которая появляется в деталь-представление-контроллер. Это как настроить изображения,цветного изображения,текст, цвет текста, и шрифт кнопки "Назад".


чтобы изменить изображение, цвет изображения, цвет текста или шрифт глобально, поместите следующее в место, которое называется перед созданием любого из ваших контроллеров вида (например,application:didFinishLaunchingWithOptions: - это хорошее место).

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    UINavigationBar* navigationBarAppearance = [UINavigationBar appearance];

    // change the back button, using default tint color
    navigationBarAppearance.backIndicatorImage = [UIImage imageNamed:@"back"];
    navigationBarAppearance.backIndicatorTransitionMaskImage = [UIImage imageNamed:@"back"];

    // change the back button, using the color inside the original image
    navigationBarAppearance.backIndicatorImage = [[UIImage imageNamed:@"back"] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
    navigationBarAppearance.backIndicatorTransitionMaskImage = [UIImage imageNamed:@"back"];

    // change the tint color of everything in a navigation bar
    navigationBarAppearance.tintColor = [UIColor greenColor];

    // change the font in all toolbar buttons
    NSDictionary *barButtonTitleTextAttributes =
    @{
      NSFontAttributeName: [UIFont fontWithName:@"HelveticaNeue-Light" size:12.0],
      NSForegroundColorAttributeName: [UIColor purpleColor]
      };

    [[UIBarButtonItem appearance] setTitleTextAttributes:barButtonTitleTextAttributes forState:UIControlStateNormal];

    return YES;
}

Примечание, Вы можете использовать appearanceWhenContainedIn: чтобы иметь больше контроля над тем, какой вид контроллеров затронутых этими изменениями, но имейте в виду, что вы не можете пройти [DetailViewController class], потому что он содержится внутри UINavigationController, а не ваш DetailViewController. Это означает, что вам нужно будет подкласс UINavigationController если вы хотите больше контроля над тем, что влияет.

чтобы настроить текст или шрифт / цвет определенного элемента кнопки назад, вы должны сделать это в MasterViewController (не DetailViewController!). Это кажется неинтуитивным, потому что кнопка появляется на DetailViewController. Однако, как только вы поймете, что способ настроить его-это установить свойство на navigationItem, это начинает иметь больше смысла.

- (void)viewDidLoad { // MASTER view controller
    [super viewDidLoad];

    UIBarButtonItem *buttonItem = [[UIBarButtonItem alloc] initWithTitle:@"Testing"
                                                                   style:UIBarButtonItemStylePlain
                                                                  target:nil
                                                                  action:nil];
    NSDictionary *barButtonTitleTextAttributes =
    @{
      NSFontAttributeName: [UIFont fontWithName:@"HelveticaNeue-Light" size:12.0],
      NSForegroundColorAttributeName: [UIColor purpleColor]
      };
    [buttonItem setTitleTextAttributes:barButtonTitleTextAttributes forState:UIControlStateNormal];
    self.navigationItem.backBarButtonItem = buttonItem;
}

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

создайте класс 'TTNavigationViewController' который является подклассом 'UINavigationController' и сделайте свой существующий навигационный контроллер этого класса либо в раскадровке / классе, пример кода в классе -

    class TTNavigationViewController: UINavigationController, UIGestureRecognizerDelegate {

override func viewDidLoad() {
    super.viewDidLoad()
    self.setNavigationBarHidden(true, animated: false)

    // enable slide-back
    if self.responds(to: #selector(getter: UINavigationController.interactivePopGestureRecognizer)) {
        self.interactivePopGestureRecognizer?.isEnabled = true
        self.interactivePopGestureRecognizer?.delegate  = self
    }
}

func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
    return true
}}