iOS9弹出窗口总是指向锚点的左上角

35

我正在使用一个故事板(segue),将一个视图控制器呈现为弹出窗口(popover)。该segue具有自定义的UIView作为其锚点。在iOS9之前,popover会正确地指向自定义UIView的中心底部(在UIView下方呈现)。但是在iOS9上,它指向UIView的左上角。

我尝试追踪所有对自定义UIView的选择器调用,以查找是否需要在我的自定义UIView中实现任何内容来提供popover的“热点”,但我找不到任何东西。

有什么想法吗?谢谢

感谢@Igor Camilo的回复 - 如果对某些人有用,这就是我在代码中修复它的方式:

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {

     UIPopoverPresentationController* possiblePopOver = segue.destinationViewController.popoverPresentationController;
     if (possiblePopOver != nil) {
         //
         // iOS9 -- ensure correct sourceRect
         //
         possiblePopOver.sourceRect = possiblePopOver.sourceView.bounds;
     }
    ...
 }

示例: '短'按钮触发一个弹出窗口,弹出窗口指向'Sort'控件的左上角

弹出窗口示意图

Segue设置


你不能使用 UIPopoverArrowDirection 常量吗?其他所有内容都已过时。 - noobsmcgoobs
嗨,问题不在于方向 - 在故事板连线中已经正确定义了。问题在于箭头指向的位置。 - Nikos
那么它指向原点,没有办法进行调整吗?箭头是否覆盖在UIView上方或者是偏离它? - noobsmcgoobs
刚刚在我的问题上添加了一个示例gif - 您可以看到弹出窗口指向“排序”按钮的左上角。 - Nikos
你正在使用UIPopoverPresentationController还是UIPopoverController?PresentationController具有sourceRect和sourceView属性,应该允许您调整锚点。 - noobsmcgoobs
我正在使用一个故事板segue,在iPad上呈现一个弹出窗口。请参见第二张图片中的设置。这在iOS8上运行得很好。我怀疑在iOS9中有些变化,从锚点获取sourceRect必须依赖于我的自定义UIView上缺失的选择器/属性/任何东西(排序按钮是自定义UIControl)。 - Nikos
9个回答

35

我遇到了完全相同的问题。我通过在prepareForSegue中设置sourceRect来解决它:

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
    switch segue.identifier {
    case "Popover Identifier"?:
        if #available(iOS 9.0, *) {
            segue.destinationViewController?.popoverPresentationController?.sourceRect = anchorView.bounds
        }
    default:
        break
    }
}

1
你从哪里获取到 anchorView 的? - Buntylm
我使用Objective-C更新了Igor Camilo的答案。这应该可以回答你的问题,@BuntyMadan。 - zath
@BuntyMadan 在我的情况下,我的控制器中只有一个弹出框,因此我从我的IBOutlets获取了锚视图,但我相信您可以从senderpopoverPresentationController?.sourceView获取它。不过我没有测试过。 - Igor Camilo
3
我认为应该使用anchorView.bounds,而不是anchorView.frame。请参见https://dev59.com/pF0a5IYBdhLWcg3wqaRS#30067716。 - stephent

18

我也遇到了同样的问题,但我的应用程序有很多弹出窗口,所以我创建了一个集中处理修复的函数(但仍然需要在每个具有弹出窗口的视图控制器上使用它)。

// Fix for IOS 9 pop-over arrow anchor bug
// ---------------------------------------
// - IOS9 points pop-over arrows on the top left corner of the anchor view
// - It seems that the popover controller's sourceRect is not being set
//   so, if it is empty  CGRect(0,0,0,0), we simply set it to the source view's bounds
//   which produces the same result as the IOS8 behaviour.
// - This method is to be called in the prepareForSegue method override of all
//   view controllers that use a PopOver segue
//
//   example use:
//
//          override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) 
//          {
//             fixIOS9PopOverAnchor(segue)   
//          }
//      
extension UIViewController
{
   func fixIOS9PopOverAnchor(segue:UIStoryboardSegue?)
   {
      guard #available(iOS 9.0, *) else { return }          
      if let popOver = segue?.destinationViewController.popoverPresentationController,
         let anchor  = popOver.sourceView
         where popOver.sourceRect == CGRect()      
            && segue!.sourceViewController === self 
      { popOver.sourceRect = anchor.bounds }   
   }       
}

8
这是Igor Camilo在Objective-C中的代码片段示例。
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    // If the sender is a UIView, we might have to correct the sourceRect of
    // a potential popover being presented due to an iOS 9 bug. See:
    // https://openradar.appspot.com/22095792 and https://dev59.com/t1wY5IYBdhLWcg3waXAE#32698841
    if ([sender isKindOfClass:UIView.class]) {
        // Fetch the destination view controller
        UIViewController *destinationViewController = [segue destinationViewController];

        // If there is indeed a UIPopoverPresentationController involved
        if ([destinationViewController respondsToSelector:@selector(popoverPresentationController)]) {
            // Fetch the popover presentation controller
            UIPopoverPresentationController *popoverPresentationController =
            destinationViewController.popoverPresentationController;

            // Set the correct sourceRect given the sender's bounds
            popoverPresentationController.sourceRect = ((UIView *)sender).bounds;
        }
    }
}

不幸的是,这个方案不适用于tvOS,因为没有UIPopoverPresentationController类。 - Duck

7
这是我的解决方案,放在prepareForSegue方法内部:
segue.destinationViewController.popoverPresentationController?.sourceRect = CGRectMake(anchorView.frame.size.width/2, anchorView.frame.size.height, 0, 0)

这将把指针移到锚视图的底部中间。

1
我也遇到了这个问题,但是现在它已经可以工作了,当我将这个添加到我的PrepareForSegue函数中时。考虑到我的Segue ID包含字符串Popover
if ([[segue identifier] containsString:@"Popover"]) {
    [segue destinationViewController].popoverPresentationController.sourceRect = self.navigationItem.titleView.bounds;
}

0
如果您在主UIViewController中引用了弹出窗口UIViewController,则可以调整sourceRect属性以偏移弹出窗口。例如,给定弹出窗口popoverVC,您可以这样做:
float xOffset = 10.0;
float yOffset = 5.0;
popoverVC.popoverPresentationController.sourceRect = CGMakeRect(xOffset, yOffset, 0.0, 0.0);

0
尝试设置源矩形(UIView或UIBarButtonItem)的宽度和高度锚点,并将其设置为活动状态。在初始化UIView或UIBarButtonItem时进行设置。
UIBarButtonItem
 [[youruibarbuttonitem.customView.widthAnchor constraintEqualToConstant:youruibarbuttonitem.customView.bounds.size.width] setActive:YES];
 [[youruibarbuttonitem.customView.heightAnchor constraintEqualToConstant:youruibarbuttonitem.customView.bounds.size.height] setActive:YES];

UIView

[[uiview.widthAnchor constraintEqualToConstant:uiview.bounds.size.width] setActive:YES];
 [[uiview.heightAnchor constraintEqualToConstant:uiview.bounds.size.height] setActive:YES];

0

关于Swift 3的更新答案!请原谅所有的转换

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if segue.identifier == "popSegue" {
            let popoverViewController = segue.destination
            popoverViewController.popoverPresentationController?.delegate = self
            segue.destination.popoverPresentationController?.sourceRect = ((sender as? UIButton)?.bounds)!
        }
    }

0

只是为了更新一个带有可用代码的实际示例,适用于任何UIView

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    switch segue.identifier {
    case "PopoverSegueId"?:
        if #available(iOS 9.0, *) {
            segue.destination.popoverPresentationController?.sourceRect = (segue.destination.popoverPresentationController?.sourceView?.bounds)!
        }
    default:
        break
    }

}

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