ユーザーがナビゲーションバーのタイトルをタップしたときにタップを認識できるように誰か助けてくださいませんか?
このタップを認識してからtableHeaderViewをアニメーション化したいと思います。おそらくTableViewを下にスライドさせます。
その後、ユーザーは(tableViewHeaderから)クイックオプションを選択してTableViewを再設定できるという考え方です。
ただし、タップを認識できません。
私はSwiftを使用しています。
ありがとうございました。
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ポイントなので)。
これは非常にエレガントではありませんが、解決策です。ストーリーボードで、タイトルの上に通常のUIButtonを配置し、ViewControllerのIBActionに添付します。ビューごとにこれを行う必要がある場合があります。
回答から、これを行うには2つのアプローチがあることがわかります。
tapGestureRecognizer
をtitleView
に追加します。これはエレガントではないようで、ナビゲーションバーのタイトルフォントを手動で設定する必要があるため、お勧めしません。tapGestureRecognizer
をnavigationBar
に追加します。これは非常にエレガントに思えますが、このアプローチを採用する投稿された回答の問題は、ナビゲーションバー内のコントロールが機能しなくなることです。コントロールが機能し続けることを可能にするこのメソッドの実装です。// 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")
}
ブルーノの答えは私にとって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")
}
ジェスチャ認識機能を使用した、よりシンプルでエレガントなソリューションがあります(少なくともiOS 9以降)。
UITapGestureRecognizer * titleTapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(titleTapped)];
[self.navigationItem.titleView addGestureRecognizer:titleTapRecognizer];
次に、タイトルタップメソッドを追加します。
-(void) titleTapped {
// Called when title is tapped
}
単純なアプローチは、タップジェスチャレコグナイザーを作成し、それをナビゲーションバー要素に添付するだけです。
// on viewDidLoad
let tapGestureRecognizer = UITapGestureRecognizer(target:self, action: #selector(YourViewController.somethingWasTapped(_:)))
self.navigationController?.navigationBar.addGestureRecognizer(tapGestureRecognizer)
func somethingWasTapped(_ sth: AnyObject){
print("Hey there")
}
navigationItem.titleView
はUIView
であるという点で、私は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")
}
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を返す必要があるのでしょうか?