Reconocer toque en el título de la barra de navegación


¿Podría alguien ayudarme a reconocer un toque cuando un usuario toca el Título en la Barra de navegación ?

Me gustaría reconocer este toque y luego animar el tableHeaderView que aparece. Posiblemente deslizando la vista de la mesa hacia abajo.

La idea es que el usuario puede seleccionar una opción rápida (de la tableViewHeader) para volver a llenar la tableView.

Sin embargo, no puedo reconocer ningún taps.

Estoy usando Swift.

Gracias usted.

Author: rob mayoff, 2014-10-10

7 answers

UINavigationBar no expone su jerarquía de vistas interna. No hay ninguna forma compatible de obtener una referencia a UILabel que muestre el título.

Podría rootear en su jerarquía de vistas "manualmente" (buscando a través de su subviews), pero eso podría dejar de funcionar en una futura versión de iOS porque la jerarquía de vistas es privada.

Una solución es crear un UILabel y establecerlo como navigationItem.titleView de su controlador de vista. Depende de usted para que coincida con el estilo de la etiqueta predeterminada, que puede cambiar en diferentes versiones de iOS.

Dicho esto, es bastante fácil de configurar:

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

Estoy ajustando el tamaño de la etiqueta a su ancho natural (usando sizeThatFits:), pero estoy ajustando su altura a 500. La barra de navegación mantendrá el ancho pero reducirá la altura a la altura propia de la barra. Esto maximiza el área disponible para tocar (ya que la altura natural de la etiqueta puede ser de solo ~22 puntos, pero la barra es de 44 puntos de altura).

 46
Author: rob mayoff,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-24 12:07:11

Esta es una solución, aunque no muy elegante. En el storyboard simplemente coloque un UIButton regular sobre el título y adjúntelo a un IBAction en su ViewController. Es posible que tenga que hacer esto para cada vista.

 17
Author: Steve Rosenberg,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-10-09 21:55:02

La respuesta de Bruno fue del 90% para mí. Una cosa que noté, sin embargo, fue que la funcionalidad de UIBarButtonItem para el Controlador de navegación dejó de funcionar en otros Controladores de vista, una vez que se agregó este reconocedor de gestos. Para arreglar esto, simplemente elimino el gesto del Controlador de navegación cuando la vista se está preparando para desaparecer:

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

}
 6
Author: megaBreezy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-04-03 21:04:00

De las respuestas, podemos decir que hay dos enfoques para hacer esto.

  1. Añadiendo un tapGestureRecognizer al enfoque titleView. Este no parece elegante y requiere que toque la fuente del título de la barra de navegación, por lo que no lo recomendaría.
  2. Añadiendo un método tapGestureRecognizer a navigationBar. Este parece bastante elegante, pero el problema con las respuestas publicadas que toman este enfoque es que todos resultan en la prevención de los controles dentro de la barra de navegación de trabajo. Aquí está mi implementación de este método que permite a sus controles para seguir trabajando.

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

}

P. S. Por favor, elimine los comentarios si este código va a entrar en producción.

 5
Author: Zia,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-05-03 13:15:05

Existe una solución más simple y elegante que utiliza reconocedores de gestos (al menos para iOS 9 y superiores).

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

Luego agregue el método tap:

-(void) titleTapped {
    // Called when title is tapped
}
 4
Author: Ben Smiley,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-09-12 09:37:44

Un enfoque simple puede ser simplemente crear el reconocedor de gestos tap y adjuntarlo a su elemento de la barra de navegación.

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

func somethingWasTapped(_ sth: AnyObject){
    print("Hey there")
}
 3
Author: Bruno,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-02-02 12:40:10

Con respecto a navigationItem.titleView es un UIView de hecho, terminé usando un UIButton que da toda la flexibilidad fuera de la caja.

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")
}
 1
Author: Mahmut C,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-06-16 20:35:32