UIPopover presentFromBarButton не по центру

#ios #uibarbuttonitem #uipopovercontroller

#iOS #элемент uibarbuttonitem #uipopovercontroller

Вопрос:

Я заметил в проекте, что расположение всплывающего окна над элементом кнопки правой панели по какой-то причине смещено вправо. Я попытался вместо этого использовать UIButton для пользовательского представления, а затем представить всплывающее окно с помощью этой кнопки, но всплывающее окно, похоже, игнорирует showFromRect, если я действительно предоставляю ему значение ‘centered’.

Не по центру всплывающее окно

Код, лежащий в основе этого, довольно прост:

 - (void)viewDidLoad {
    [super viewDidLoad];

     UIBarButtonItem *button = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"share"] style:UIBarButtonItemStylePlain target:self action:@selector(shareTap:)];

     self.navigationItem.rightBarButtonItem = button;
}

- (void)shareTap:(UIBarButtonItem *)button {
    self.popover = [[UIPopoverController alloc] initWithContentViewController:[[UIViewController alloc] init]];

    [self.popover presentPopoverFromBarButtonItem:button permittedArrowDirections:UIPopoverArrowDirectionUp animated:YES];
}
  

Теперь, если я переключусь на использование внутренней кнопки, как уже упоминалось, я вижу аналогичное поведение (обратите внимание на изменение цвета, чтобы отображалось изображение).

использование внутренней кнопки

Код для этого по-прежнему довольно прост:

 - (void)viewDidLoad {
    [super viewDidLoad];

    UIButton *innerButton = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 22, 22)];
    [innerButton setImage:[UIImage imageNamed:@"share"] forState:UIControlStateNormal];
    [innerButton addTarget:self action:@selector(shareTap:) forControlEvents:UIControlEventTouchUpInside];

    self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:innerButton];;
}

- (void)shareTap:(UIButton *)button {
    self.popover = [[UIPopoverController alloc] initWithContentViewController:[[UIViewController alloc] init]];

//    CGRect bottomCenter = CGRectMake(button.frame.size.width / 2, button.frame.size.height, 1, 1);
    CGRect bottomCenter = CGRectMake(2, button.frame.size.height, 1, 1);

    [self.popover presentPopoverFromRect:bottomCenter inView:button permittedArrowDirections:UIPopoverArrowDirectionUp animated:YES];
}
  

Обратите внимание, что вместо фактического центра я использовал произвольное значение 2. Если я использую 1 или 0, всплывающее окно выравнивается по левому краю. Все, что больше единицы, снова выравнивается по правому краю.

выровнено по левому краю

Я немного поискал в поисках похожего вопроса, но, похоже, не могу найти никого с такой же проблемой. Любая идея о том, что вызывает это или как этого избежать, была бы высоко оценена. Единственное, о чем я смог догадаться, это то, что из-за его близости к краю Apple делает некоторые позиционирующие вуду, чтобы заставить его быть там, где они этого хотят. Проблема в том, что кнопка в правом верхнем углу выглядит как довольно стандартное выпадающее положение.

Редактировать: я подтвердил, что такое же поведение происходит при длительном нажатии на значок «Новое сообщение» в приложении «Родная почта».

Редактировать 2: я смог подтвердить Apple, что это проблема. В одной из более поздних версий (я не могу вспомнить, какой, я думаю, одной из 9-х) они сделали так, чтобы вы могли установить это вручную. Я полагаю, что поведение по умолчанию по-прежнему неверно (я не пробовал это какое-то время), но вы можете использовать метод смещения CGRect, чтобы заставить его работать правильно, если вы так склонны.

Комментарии:

1. Попробуйте этот UIBarButtonItem *button = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:цель действия UIBarButtonSystemItem:self action:@selector (Общий доступ:)];

2. Не могли бы вы объяснить немного больше? Использование системного элемента само по себе не исправляет размещение кнопки, и в моем редактировании я упомянул, что приложения по умолчанию также демонстрируют проблему (Почта), которые предположительно используют системные значки.

Ответ №1:

Вот один из способов сделать это: вместо того, чтобы использовать UIBarButtonSystemItem , используйте UIBarButtonItem с пользовательским представлением. Просто перетащите UIButton в UINavigationBar , чтобы получить UIBarButtonItem со встроенным UIButton , который отображается как UIBarButtonItem пользовательский вид.

 @IBAction func didTapActionButton(_ sender: Any) {
    if let vc = self.storyboard?.instantiateViewController(withIdentifier: "myPopover") {
        vc.modalPresentationStyle = .popover
        guard let innerView = actionButton.customView else {
            // Unexpected missing custom view
            return
        }

        vc.popoverPresentationController?.sourceView = navigationController?.navigationBar
        vc.popoverPresentationController?.sourceRect = innerView.frame
        self.present(vc, animated: true, completion: nil)
    }
}
  

Комментарии:

1. Я думаю, вы могли заметить, что это исправлено после iOS 9. Я упоминал в начале своего поста, что даже UIButton не работал, но позже (при редактировании) Apple обновила его, чтобы вы могли исправить позиционирование с помощью UIButton.

2. У меня была проблема со смещением стрелки всплывающего окна, которую вы описали изначально в проекте на iOS 10, и это было для меня исправлением — я предполагаю, что вы говорите, что этот метод не будет работать на iOS 9, где присутствовала ошибка. Я этого не пробовал.

Ответ №2:

Вы должны указать рамку кнопки панели, из которой должно быть представлено всплывающее окно.

Следующий код представляет UIAlertController как всплывающее окно из кнопки панели.

     @IBOutlet weak var playerRightBarButton: UIBarButtonItem!

    extension UIBarButtonItem {
       var frame: CGRect? {
        guard let view = self.value(forKey: "view") as? UIView else {
          return nil
      }
      return view.frame
    }
  }

    let alert = UIAlertController(title: "", message: "Sample PopOver", 
    preferredStyle: .actionSheet)
    if alert.responds(to: #selector(getter: popoverPresentationController)) {
            alert.popoverPresentationController?.sourceView = self.view
            alert.popoverPresentationController?.barButtonItem = playerRightBarButton
            alert.popoverPresentationController?.permittedArrowDirections = .up
            if let frame = playerRightBarButton.frame {
                alert.popoverPresentationController?.sourceRect = frame
            }
        }
        self.present(alert, animated: true, completion: nil)
  

Комментарии:

1. Я не совсем уверен, но я не думаю, что это связано с моим вопросом. Как это влияет на размещение стрелки? Какой «фрейм» вы имеете в виду для настройки? Вы говорите о добавлении этого метода расширения в класс UIBarButtonItem?

2. Расширение предоставляет вам рамку кнопки. Когда мы устанавливаем фрейм, из которого необходимо представить всплывающее окно, он соответствующим образом центрируется. Я привел пример AlertController, представляющего собой всплывающее окно в iPad.