web-dev-qa-db-ja.com

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を設定しようとしましたが、これは私のカスタム画像を表示していないようです。

79
lehn0058

重要:これはハックです。これをご覧になることをお勧めします answer

leftBarButtonItemを割り当てた後、次の行を呼び出すとうまくいきました:

self.navigationController.interactivePopGestureRecognizer.delegate = self;

編集:initメソッドで呼び出された場合、これは機能しません。 viewDidLoadまたは同様のメソッドで呼び出す必要があります。

77
Paul Hunter

可能な限り、UINavigationBarのbackIndicatorImageおよびbackIndicatorTransitionMaskImageプロパティを使用します。 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のデリゲートを操作しようとすると、多くの問題が発生します。

56
Saltymule

このソリューションを見ました http://keighl.com/post/ios7-interactive-pop-gesture-custom-back-button/ UINavigationControllerをサブクラス化します。コントローラーが配置される前にスワイプする場合を処理するため、より優れたソリューション-クラッシュを引き起こします。

これに加えて、ルートビューコントローラーでスワイプを行うと(1つを押してからもう一度戻ると)、UIが応答しなくなります(上記の回答でも同じ問題)。

したがって、サブクラス化された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
29
Nick H247

私が使う

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

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

ここに Nick H247の答え のSwift3バージョンがあります

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 {}
6
duan
navigationController.interactivePopGestureRecognizer.delegate = (id<UIGestureRecognizerDelegate>)self;

これは http://stuartkhall.com/posts/ios-7-development-tips-tricks-hacks からのものですが、いくつかのバグが発生します。

  1. 画面の左端からスワイプするときに、別のviewControllerをnavigationControllerにプッシュします。
  2. または、topViewControllerがnavigationControllerからポップアップしているときに、画面の左端からスワイプします。

例えばnavigationControllerのrootViewControllerが表示されたら、画面の左端からスワイプインし、何かを(すばやく)タップしてanotherViewControllerをnavigationControllerにプッシュしてから、

  • RootViewControllerは、タッチイベントに応答しません。
  • AnotherViewControllerは表示されません。
  • 画面の端からもう一度スワイプすると、anotherViewControllerが表示されます。
  • カスタムバックボタンをタップしてanotherViewControllerをポップし、クラッシュします!

そのため、self.navigationController.interactivePopGestureRecognizer.delegateUIGestureRecognizerDelegateメソッドを次のように実装する必要があります。

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

また、戻るボタンを非表示にして、カスタムのleftBarItemに置き換えます。
Pushアクションが機能した後、interactivePopGestureRecognizerデリゲートを削除します。

[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;
}
6
avishic

self.navigationController.interactivePopGestureRecognizer.enabled = YES;

3
ilya n.

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 {}
1
PW486

このロジックを使用して、スワイプジェスチャーの有効化または無効化を維持します。

- (void)navigationController:(UINavigationController *)navigationController
       didShowViewController:(UIViewController *)viewController
                    animated:(BOOL)animate
{
    if ([self.navigationController respondsToSelector:@selector(interactivePopGestureRecognizer)])
    {
        if (self.navigationController.viewControllers.count > 1)
        {
            self.navigationController.interactivePopGestureRecognizer.enabled = YES;
        }
        else
        {
            self.navigationController.interactivePopGestureRecognizer.enabled = NO;
        }
    }
}

私はこれを書きませんでしたが、次のブログは非常に役立ち、カスタムナビゲーションボタンに関する問題を解決しました。

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

要約すると、ポップジェスチャデリゲートを使用するカスタムUINavigationControllerを実装しています。非常にきれいでポータブル!

コード:

@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;
}
1
kgaidis

インタラクティブポップジェスチャのデリゲートとして現在のView Controllerを割り当てていたが、プッシュされたビューまたはナビゲーションスタックのビューの下のビューでジェスチャが壊れるという同様の問題がありました。これを解決する方法は、デリゲートを-viewDidAppearに設定し、-viewWillDisappearでnilに設定することでした。これにより、他のビューが正しく機能するようになりました。

0
Bill Burgess

Appleのデフォルトのマスター/詳細プロジェクトテンプレートを使用していることを想像してください。マスターはテーブルビューコントローラーで、タップすると詳細ビューコントローラーが表示されます。

Detail View Controllerに表示される戻るボタンをカスタマイズします。これは、戻るボタンの imageimage colortext 、text color、および font をカスタマイズする方法です。 。


画像、画像の色、テキストの色、またはフォントをグローバルに変更するには、View Controllerが作成される前にと呼ばれる場所に以下を配置します (例: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:を使用して、これらの変更の影響を受けるView Controllerをより細かく制御できますが、[DetailViewController class]を渡すことはできないことに注意してください。 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;
}

注:self.navigationItem.backBarButtonItemの設定後にtitleTextAttributesを設定しようとしても機能しないようです。したがって、このプロパティに値を割り当てる前に設定する必要があります。

0
Senseful

「UINavigationController」のサブクラスであるクラス「TTNavigationViewController」を作成し、このクラスの既存のNavigation Controllerをストーリーボード/クラス、クラスのコード例のいずれかに作成します-

    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
}}
0
Prakash Raj