【问题标题】:Recognize tap on navigation bar title识别点击导航栏标题
【发布时间】:2014-12-04 21:49:10
【问题描述】:

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

我想识别这个点击,然后为出现的 tableHeaderView 设置动画。可能会向下滑动 TableView。

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

但是我无法识别任何水龙头。我正在使用 Swift。

【问题讨论】:

    标签: ios swift uinavigationcontroller uitapgesturerecognizer


    【解决方案1】:

    只需在自定义标题视图上放置一个透明 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)....
    

    【讨论】:

      【解决方案2】:

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

      1. UITapGestureRecognizer 添加到titleView。这看起来不太优雅,需要您手动设置导航栏标题字体,所以我不推荐它。
      2. UITapGestureRecognizer 添加到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")
      
      }
      

      【讨论】:

      • 我不知道sender.location(in 这是一个聪明的方法。我投票不够。
      • 不错的解决方案。我喜欢 viewWillDisappear 它为子视图删除它。不错!
      【解决方案3】:

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

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

      【讨论】:

      • 更新:我的解决方法似乎只适用于 iOS 9。在 iOS 12 中,hitView 未被识别为我的 barButton。我没有 iOS 12 开发设备,因此无法调试。但我猜测在 iOS 12 中禁用的 barButton 不会被视为 UIControl,或者返回的 hitView 是底层的 navigationBar。
      【解决方案4】:

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

      【讨论】:

        【解决方案5】:

        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 磅)。

        【讨论】:

        • 完美解决方案。我完全同意不搜索视图层次结构,就像我发现的其他示例一样,因为将来可能无法使用。非常感谢。
        • 我一个人就快到了,但没有怀疑userInteractionEnabled。 +1。
        • 我对这个解决方案有疑问。在稍后更新 uilabel 中的文本时,由于某种原因,标签本身会跳转到 x 0(屏幕的最左侧)。似乎是 UIKit 中的一个错误。反正在身边? (iOS 10.1.1)
        • 我无法 100% 重现上述问题,但是如果我在更改 UILabel:[self.navigationItem.titleView.superview setNeedsLayout]; 的文本后这样做,现在它似乎已经消失了。它确实阻止了标签跳到 X 0。
        • 那是我的建议。
        【解决方案6】:

        布鲁诺的回答对我来说是 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")
        
        }
        

        【讨论】:

        • 如果主要是点击导航栏而不是标题,那将是一个更简单的解决方案!它还重用了现有的 UIView,不需要引入新的视图。
        【解决方案7】:

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

        // on viewDidLoad
        let tapGestureRecognizer = UITapGestureRecognizer(target:self, action: #selector(YourViewController.somethingWasTapped(_:)))
        self.navigationController?.navigationBar.addGestureRecognizer(tapGestureRecognizer)
        
        func somethingWasTapped(_ sth: AnyObject){
            print("Hey there")
        }
        

        【讨论】:

        • 最好的方法。
        • 在我的情况下,在另一个视图控制器被推入堆栈后,这破坏了后退按钮的功能。
        • 它会破坏导航栏中其他按钮的功能
        • 请看我的回答。它采用这种方法,但保持导航栏中的按钮正常工作。
        • 百万美元答案(Y)
        【解决方案8】:

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

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

        然后添加标题点击方法:

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

        【讨论】:

        • 不要忘记在接受的答案中定义self.navigationItem.titleView。默认值为 nil。
        【解决方案9】:

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

        【讨论】:

        • 这是一个很好的解决方案,实际上,完美且易于实施。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-08-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多