UIPopover如何从UIBarButtonItem偏离中心位置弹出?

18

我注意到在一个项目中,弹出窗口的定位在右侧按钮上似乎有些偏右。我尝试使用一个UIButton作为自定义视图代替,然后从该按钮呈现弹出窗口,但是如果我实际提供了“居中”值,弹出窗口似乎会忽略showFromRect。

偏离中心的弹出窗口

这背后的代码非常简单:

- (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,弹出框将左对齐,而大于1的任何值都将再次右对齐。

left-aligned

我搜索了一下类似的问题,但似乎没有找到有相同问题的人。任何关于导致这种情况的想法或如何避免它的建议都将不胜感激。我唯一能猜测的就是由于靠近边缘,Apple进行了某些定位操作以强制它呈现他们想要的效果。问题在于右上角的按钮看起来像一个相当标准的下拉位置。

编辑:我确认,在本机邮件应用程序中长按“新消息”图标会出现相同的行为。

编辑2:我已经向苹果确认了这个问题。在最近的某个版本中(我记不得是哪一个了,我想是9之一),他们使您可以手动设置此项。我认为默认行为仍然有问题(我已经有一段时间没有尝试过),但是您可以使用CGRect offset方法使其正常工作,如果您有兴趣的话。


尝试这个: UIBarButtonItem *button = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAction target:self action:@selector(shareTap:)]; - Nishant
1
你能否详细解释一下?单纯使用系统图标并不能自动修复按钮位置问题。另外,在我的编辑中我提到了默认应用程序(如Mail)同样存在该问题,这些应用程序可能也在使用系统图标。 - shortstuffsushi
2个回答

1
这里有一种做法:不使用UIBarButtonSystemItem,而是使用带自定义视图的UIBarButtonItem。只需将一个UIButton拖进UINavigationBar即可得到嵌入UIButtonUIBarButtonItem,它会显示为UIBarButtonItem的customView。
@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)
    }
}

我认为你可能已经注意到这是在iOS 9之后固定的。我在帖子开头提到,即使UIButton也无法工作,但后来(在编辑中)苹果更新了它,以便您可以使用UIButton修复位置。 - shortstuffsushi
我曾经在iOS 10项目中遇到了你最初描述的弹出箭头偏移问题,而这个方法对我有用 - 我猜你的意思是这个方法在存在该漏洞的iOS 9上不起作用。我没有尝试过。 - stevex

0

您必须指定弹出窗口需要从哪个栏按钮的框架中呈现。

以下代码将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)

我不是很确定,但我认为这与我的问题无关。这如何解决箭头位置的问题?你所说的“框架”是指什么?你是在谈论在UIBarButtonItem类上添加扩展方法吗? - shortstuffsushi
该扩展提供了barButton的框架。当我们设置弹出视图所需的框架时,它会自动居中。我提供了一个在iPad上以popOver方式显示alertController的示例。 - Swathi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接