Подтвердить что ты не робот

Признать красную кнопку на заголовке панели навигации

Может ли кто-нибудь помочь мне распознать нажатие, когда пользователь нажимает на Заголовок в панели навигации?

Я хотел бы узнать этот кран и затем оживить появление tableHeaderView. Возможно, сдвиг TableView вниз.

Идея состоит в том, что пользователь может затем выбрать быстрый вариант (из tableViewHeader) для повторного заполнения TableView.

Однако я не могу распознать любые нажатия.

Я использую Swift.

Спасибо.

4b9b3361

Ответ 1

UINavigationBar не раскрывает свою внутреннюю иерархию представлений. Нет поддерживаемого способа получения ссылки на UILabel, который отображает заголовок.

Вы можете root в своей иерархии представлений "вручную" (путем поиска через его 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 точки).

Ответ 2

Это решение, хотя и не очень элегантное. В раскадровке просто поместите обычный UIButton над заголовком и присоедините его к IBAction в вашем ViewController. Возможно, вам понадобится сделать это для каждого вида.

Ответ 3

Из ответов мы можем сказать, что для этого есть два подхода.

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

}

Ответ 4

Ответ Бруно был для меня на 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")

}

Ответ 5

Простым подходом может быть просто создание распознавателя жестов и привязка его к элементу панели навигации.

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

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

Ответ 6

Существует более простое и элегантное решение с использованием распознавателей жестов (по крайней мере, для iOS 9 и выше).

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

Затем добавьте метод заголовка:

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

Ответ 7

Что касается 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")
}

Ответ 8

Зия: Ваше решение guard !(hitView is UIControl) работало хорошо для меня при работе под iOS 9, но когда я запускаю тот же код в новых версиях iOS, он не видит hitView как UIControl, когда нажатая кнопка barButton отключена.

У меня есть несколько UIBarButtonItems в моей панели навигации. Когда эти кнопки barButton включены (hitView is UIControl) в моем действии UITapGestureRecognizer работает правильно, и функция действия завершается. Но если UIBarButtonItem отключен и пользователь нажимает на кнопку, (hitView is UIControl) имеет значение false, и код выполняется так, как если бы пользователь нажал на панель навигации.

Единственный способ обойти эту проблему - это получить объект UIView для всех моих barButtons в viewWillAppear с помощью:

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

так далее...

А затем в моей функции действия UITapGestureRecognizer я тестирую:

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

Это уродливый обходной путь! Есть идеи, почему (hitView - это UIControl) должен возвращать false на отключенной кнопке панели?

Ответ 9

// 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];
    }
}