识别点击导航栏标题

问题描述 投票:34回答:9

当用户点击导航栏中的标题时,有人可以帮我识别点击吗?

我想识别这个tap,然后动画tableHeaderView出现。可能会向下滑动TableView。

这个想法是用户可以选择一个快速选项(来自tableViewHeader)来重新填充TableView。

但是我无法识别任何水龙头。

我正在使用Swift。

谢谢。

ios swift uinavigationcontroller uitapgesturerecognizer
9个回答
54
投票

UINavigationBar不公开其内部视图层次结构。没有受支持的方式来获取显示标题的UILabel的引用。

您可以“手动”查看其视图层次结构(通过搜索其subviews),但由于视图层次结构是私有的,因此可能会在未来的iOS版本中停止工作。

一个解决方法是创建一个UILabel并将其设置为您的视图控制器的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个点)。


23
投票

这是一个解决方案,虽然不是超级优雅。在故事板中只需在标题上放置一个常规UIButton,并将其附加到ViewController中的IBAction。您可能需要为每个视图执行此操作。


20
投票

从答案中,我们可以看出有两种方法可以做到这一点。

  1. 添加tapGestureRecognizertitleView。这似乎不优雅,需要您手动设置导航栏标题字体,所以我不推荐它。
  2. 添加tapGestureRecognizernavigationBar。这看起来非常优雅,但采用这种方法的已发布答案的问题在于它们都会导致阻止导航栏中的控件工作。以下是我对此方法的实现,它允许您的控件继续工作。

斯威夫特3

// Declare gesture recognizer
var tapGestureRecognizer: UITapGestureRecognizer!

override func viewWillAppear(_ animated: Bool) {

    // Add gesture recognizer to the navigation bar when the view is about to appear
    tapGestureRecognizer = UITapGestureRecognizer(target:self, action: #selector(self.navigationBarTapped(_:)))
    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")

}

7
投票

布鲁诺的答案对我来说是90%。但我注意到的一件事是,一旦添加了这个手势识别器,导航控制器的UIBarButtonItem功能就停止在其他视图控制器中工作。为了解决这个问题,我只是在视图准备消失时从导航控制器中删除手势:

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")

}

4
投票

使用手势识别器(至少对于iOS 9及更高版本),有一种更简单,更优雅的解决方案。

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

然后添加标题点击方法:

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

3
投票

一种简单的方法可能只是创建轻敲手势识别器并将其附加到导航栏元素。

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

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

2
投票

关于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")
}

0
投票

Zia:在iOS 9下运行时,你的guard !(hitView is UIControl)解决方案对我来说运行良好,但是当我在较新的iOS版本中运行相同的代码时,当禁用tapped barButton时,它无法将hitView视为UIControl。

我的导航栏中有许多UIBarButtonItems。当启用这些barButtons时,我的UITapGestureRecognizer操作中的(hitView是UIControl)正常工作并且动作函数退出。但是如果UIBarButtonItem被禁用并且用户点击按钮,则(hitView is UIControl)为false并且代码继续进行,就好像用户点击了导航栏一样。

我找到解决此问题的唯一方法是在viewWillAppear中为我的所有barButton获取UIView对象:

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

等等...

然后在我的UITapGestureRecognizer动作函数中我测试:

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

这是一个丑陋的解决方法!知道为什么(hitView是UIControl)应该在禁用的条形按钮上返回false吗?


-1
投票
// same as accepted answer but with UIButton and UINavigationBar appearance

// Swift
@objc func barTap(){
    NSLog("barTap()");
}

override func didMove(toParentViewController parent: UIViewController?){
    super .didMove(toParentViewController: parent)
    if (parent != nil && nil == self.navigationItem.titleView)
    {
        var attrs = UINavigationBar.appearance().titleTextAttributes
        if nil == attrs{
            attrs = [.foregroundColor:UIColor.darkText,
                     .font:UIFont.systemFont(ofSize: 17)]
        }
        let button = UIButton.init(frame: CGRect.zero)
        button.setTitle(self.title, for: .normal)
        button.setTitleColor((attrs![.foregroundColor] as! UIColor), for: .normal)
        button.setTitleColor((attrs![.foregroundColor] as! UIColor).withAlphaComponent(0.33), for: .highlighted)
        button.titleLabel?.font = attrs?[.font] as? UIFont;
        self.navigationItem.titleView = button;
        button .addTarget(self, action: #selector(self.barTap), for: .touchUpInside)
    }
}

// Obj-C
- (void)didMoveToParentViewController:(UIViewController *)parent
{
    [super didMoveToParentViewController:parent];
    if (parent && !self.navigationItem.titleView)
    {
        NSDictionary *attrs = [[UINavigationBar appearance] titleTextAttributes];
        UIButton *button = [[UIButton alloc] initWithFrame:CGRectZero];
        [button setTitle:self.title forState:UIControlStateNormal];
        [button setTitleColor:attrs[NSForegroundColorAttributeName] forState:UIControlStateNormal];
        [button setTitleColor:[attrs[NSForegroundColorAttributeName] colorWithAlphaComponent:0.33f] forState:UIControlStateHighlighted];
        button.titleLabel.font = attrs[NSFontAttributeName];
        self.navigationItem.titleView = button;
        [button addTarget:self action:@selector(barTap) forControlEvents:UIControlEventTouchUpInside];
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.