web-dev-qa-db-ja.com

ナビゲーションバーのタイトルのタップを認識する

ユーザーがナビゲーションバータイトルをタップしたときにタップを認識できるように誰か助けてくださいませんか?

このタップを認識してからtableHeaderViewをアニメーション化したいと思います。おそらくTableViewを下にスライドさせます。

その後、ユーザーは(tableViewHeaderから)クイックオプションを選択してTableViewを再設定できるという考え方です。

ただし、タップを認識できません。

私はSwiftを使用しています。

ありがとうございました。

37
Richard

UINavigationBarは、内部ビュー階層を公開しません。タイトルを表示するUILabelへの参照を取得する方法はサポートされていません。

ビュー階層を「手動で」(subviewsを検索することで)回避できますが、ビュー階層はプライベートであるため、今後のiOSリリースでは動作しなくなる可能性があります。

回避策の1つは、UILabelを作成し、それをView ControllerのnavigationItem.titleView。デフォルトのラベルのスタイルを一致させるのはあなた次第です。これはiOSの異なるバージョンで変更される可能性があります。

とはいえ、設定は非常に簡単です。

override func didMove(toParentViewController parent: UIViewController?) {
    super.didMove(toParentViewController: parent)

    if parent != nil && self.navigationItem.titleView == nil {
        initNavigationItemTitleView()
    }
}

private func initNavigationItemTitleView() {
    let titleView = UILabel()
    titleView.text = "Hello World"
    titleView.font = UIFont(name: "HelveticaNeue-Medium", size: 17)
    let width = titleView.sizeThatFits(CGSize(width: CGFloat.greatestFiniteMagnitude, height: CGFloat.greatestFiniteMagnitude)).width
    titleView.frame = CGRect(Origin:CGPoint.zero, size:CGSize(width: width, height: 500))
    self.navigationItem.titleView = titleView

    let recognizer = UITapGestureRecognizer(target: self, action: #selector(YourViewController.titleWasTapped))
    titleView.userInteractionEnabled = true
    titleView.addGestureRecognizer(recognizer)
}

@objc private func titleWasTapped() {
    NSLog("Hello, titleWasTapped!")
}

ラベルのサイズを自然な幅に設定しています(sizeThatFits:)、しかし、私はその高さを500に設定しています。ナビゲーションバーは幅を維持しますが、バーの高さまで縮小します。これにより、タップできる領域が最大になります(ラベルの自然な高さは最大22ポイントですが、バーの高さは44ポイントなので)。

56
rob mayoff

これは非常にエレガントではありませんが、解決策です。ストーリーボードで、タイトルの上に通常のUIButtonを配置し、ViewControllerのIBActionに添付します。ビューごとにこれを行う必要がある場合があります。

23
Steve Rosenberg

回答から、これを行うには2つのアプローチがあることがわかります。

  1. tapGestureRecognizertitleViewに追加します。これはエレガントではないようで、ナビゲーションバーのタイトルフォントを手動で設定する必要があるため、お勧めしません。
  2. tapGestureRecognizernavigationBarに追加します。これは非常にエレガントに思えますが、このアプローチを採用する投稿された回答の問題は、ナビゲーションバー内のコントロールが機能しなくなることです。コントロールが機能し続けることを可能にするこのメソッドの実装です。

// Declare gesture recognizer
var tapGestureRecognizer: UITapGestureRecognizer!

override func viewDidLoad() {

    // Instantiate gesture recognizer
    tapGestureRecognizer = UITapGestureRecognizer(target:self, action: #selector(self.navigationBarTapped(_:)))
}

override func viewWillAppear(_ animated: Bool) {

    // Add gesture recognizer to the navigation bar when the view is about to appear
    self.navigationController?.navigationBar.addGestureRecognizer(tapGestureRecognizer)

    // This allows controlls in the navigation bar to continue receiving touches
    tapGestureRecognizer.cancelsTouchesInView = false
}

override func viewWillDisappear(_ animated: Bool) {

    // Remove gesture recognizer from navigation bar when view is about to disappear
    self.navigationController?.navigationBar.removeGestureRecognizer(tapGestureRecognizer)
}

// Action called when navigation bar is tapped anywhere
@objc func navigationBarTapped(_ sender: UITapGestureRecognizer){

    // Make sure that a button is not tapped.
    let location = sender.location(in: self.navigationController?.navigationBar)
    let hitView = self.navigationController?.navigationBar.hitTest(location, with: nil)

    guard !(hitView is UIControl) else { return }

    // Here, we know that the user wanted to tap the navigation bar and not a control inside it 
    print("Navigation bar tapped")

}
21
Zia

ブルーノの答えは私にとって90%でした。ただし、私が注意したことの1つは、このジェスチャー認識エンジンが追加されると、Navigation ControllerのUIBarButtonItem機能が他のView Controllerで機能しなくなることでした。これを修正するには、ビューが消える準備をしているときに、Navigation Controllerからジェスチャーを削除します。

var tapGestureRecognizer : UITapGestureRecognizer!

override func viewWillAppear(_ animated: Bool) {

  tapGestureRecognizer = UITapGestureRecognizer(target:self, action: #selector(self.navBarTapped(_:)))

  self.navigationController?.navigationBar.addGestureRecognizer(tapGestureRecognizer)

}

override func viewWillDisappear(_ animated: Bool) {

  self.navigationController?.navigationBar.removeGestureRecognizer(tapGestureRecognizer)

}

func navBarTapped(_ theObject: AnyObject){

  print("Hey there")

}
7
megaBreezy

ジェスチャ認識機能を使用した、よりシンプルでエレガントなソリューションがあります(少なくともiOS 9以降)。

UITapGestureRecognizer * titleTapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(titleTapped)];
[self.navigationItem.titleView addGestureRecognizer:titleTapRecognizer];

次に、タイトルタップメソッドを追加します。

-(void) titleTapped {
    // Called when title is tapped
}
4
Ben Smiley

単純なアプローチは、タップジェスチャレコグナイザーを作成し、それをナビゲーションバー要素に添付するだけです。

// on viewDidLoad
let tapGestureRecognizer = UITapGestureRecognizer(target:self, action: #selector(YourViewController.somethingWasTapped(_:)))
self.navigationController?.navigationBar.addGestureRecognizer(tapGestureRecognizer)

func somethingWasTapped(_ sth: AnyObject){
    print("Hey there")
}
3
Bruno Cunha

navigationItem.titleViewUIViewであるという点で、私はUIButtonを使用することになりました。

override func viewDidLoad() {

    // Create title button
    let titleViewButton = UIButton(type: .system)
    titleViewButton.setTitleColor(UIColor.black, for: .normal)
    titleViewButton.setTitle("Tap Me", for: .normal)

    // Create action listener
    titleViewButton.addTarget(self, action: #selector(YourViewController.titleViewButtonDidTap), for: .touchUpInside)

    // Set the title view with newly created button
    navigationItem.titleView = titleViewButton
}

@objc func titleViewButtonDidTap(_ sender: Any) {
    print("Title did tap")
}
2
Mahmut C

Zia:あなたのguard !(hitView is UIControl)ソリューションは、iOS 9で実行中はうまく機能していますが、新しいiOSバージョンで同じコードを実行すると、タップされたbarButtonが無効になっているときにhitViewがUIControlとして表示されません。

ナビゲーションバーにいくつかのUIBarButtonItemがあります。これらのbarButtonsが有効になると、UITapGestureRecognizerアクションの(hitViewはUIControl)が正しく機能し、アクション関数が終了します。ただし、UIBarButtonItemが無効で、ユーザーがボタンをタップすると、(hitView is UIControl)はfalseであり、ユーザーがナビゲーションバーをタップしたかのようにコードが進みます。

この問題を回避することがわかった唯一の方法は、viewWillAppearのすべてのbarButtonsのUIViewオブジェクトを取得することです。

button1View = button1Item.value(forKey: "view") as? UIView

等...

そして、UITapGestureRecognizerアクション関数でテストします:

if [button1View, button2View, button3View, button4View].contains(hitView)
{
  return
}

これはい回避策です!無効なバーボタンでなぜ(hitViewはUIControlである)falseを返す必要があるのでしょうか?

0
WholeCheese