识别点击导航栏标题

时间:2014-10-09 20:23:20

标签: ios swift uinavigationcontroller uitapgesturerecognizer

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

我想识别这个点按,然后显示 tableHeaderView 的动画。可能会将TableView向下滑动。

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

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

我正在使用Swift。

谢谢。

10 个答案:

答案 0 :(得分:53)

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个点)。

答案 1 :(得分:21)

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

答案 2 :(得分:17)

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

  1. tapGestureRecognizer添加titleView。这似乎不优雅,需要您手动设置导航栏标题字体,所以我不推荐它。
  2. tapGestureRecognizer添加到navigationBar。这看起来非常优雅,但采用这种方法的已发布答案的问题在于它们都会导致阻止导航栏中的控件工作。以下是我对此方法的实现,它允许您的控件继续工作。
  3. Swift 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")
    
    }
    

答案 3 :(得分: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 :(得分:4)

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

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

然后添加标题点击方法:

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

答案 5 :(得分:3)

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

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

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

答案 6 :(得分: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")
}

答案 7 :(得分:0)

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

我的导航栏中有许多UIBarButtonItems。启用这些barButton时,我的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吗?

答案 8 :(得分:0)

只需在自定义标题视图上放置一个透明的 UIButton。

    let maskButton = UIButton(frame: .zero)
    // ....
    // your views...
    // add Transparent button at last
    addSubview(maskButton)
    maskButton.snp.makeConstraints({
        $0.edges.equalToSuperview()
    })
    // listener tap event 
    maskButton.addTarget(self,action:#selector(buttonClicked)....

答案 9 :(得分:-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];
    }
}