当 iPad 旋转时,ActionSheet 的 PopoverController 不会停留在窗口的中心

Posted

技术标签:

【中文标题】当 iPad 旋转时,ActionSheet 的 PopoverController 不会停留在窗口的中心【英文标题】:ActionSheet's PopoverController doesn't stay in window's center when iPad is rotated 【发布时间】:2018-05-04 17:45:28 【问题描述】:

我有一个 logOut 按钮,它显示了一个操作表,该操作表使用以下代码将 iPad 上的弹出框直接置于窗口中间:

popoverController.sourceRect = CGRect(x: window.bounds.midX, y: window.bounds.midY, width: 0, height: 0)
popoverController.permittedArrowDirections = []

这里显示在窗口中间:

但是当我旋转 iPad 时,它会将自己定位在左下角:

为什么当我旋转 iPad 时它不停留在窗口的中心?如何修复它以使其保持旋转中心?

操作表:

fileprivate func logOut()

    let actionSheet = UIAlertController(title: nil, message: "Are you sure you want to log out?", preferredStyle: .actionSheet)

    let cancelActionButton = UIAlertAction(title: "Cancel", style: .destructive) (action) in
        // ...
    

    let logoutActionButton = UIAlertAction(title: "Log Out", style: .default) (action) in
         // ...
     

    actionSheet.addAction(logoutActionButton)
    actionSheet.addAction(cancelActionButton)

    if let popoverController = actionSheet.popoverPresentationController
        popoverController.popoverBackgroundViewClass = PopoverBackgroundView.self

        popoverController.sourceView = view
        guard let window = UIApplication.shared.keyWindow else 
            popoverController.sourceView = view
            popoverController.sourceRect = CGRect(x: view.bounds.midX, y: view.bounds.midY, width: 0, height: 0)
            popoverController.permittedArrowDirections = []
            return
        
        window.backgroundColor = .clear
        popoverController.sourceRect = CGRect(x: window.bounds.midX, y: window.bounds.midY, width: 0, height: 0)
        popoverController.permittedArrowDirections = []
    
    tabBarController?.present(actionSheet, animated: true, completion: nil)

UIPopoverBackgroundView 的子类:

class PopoverBackgroundView: UIPopoverBackgroundView 

    override init(frame: CGRect) 
        super.init(frame: frame)
        backgroundColor = UIColor.white
    

    convenience required init(coder aDecoder: NSCoder) 
        self.init(frame: CGRect.zero)
    

    override func layoutSubviews() 
        super.layoutSubviews()
    

    override var arrowOffset: CGFloat 
        get 
            return 0.0
        
        set 
        

    

    override var arrowDirection: UIPopoverArrowDirection 
        get 
            return UIPopoverArrowDirection.up
        
        set 
        
    

    override class func contentViewInsets() -> UIEdgeInsets 
        return UIEdgeInsetsMake(10.0, 10.0, 10.0, 10.0)
    

    override class func arrowBase() -> CGFloat 
        return 0.0
    

    override class func arrowHeight() -> CGFloat 
        return 0.0
    

    override class var wantsDefaultContentAppearance: Bool 
        get 
            return false
        
    

【问题讨论】:

【参考方案1】:

您应该在设备旋转时更新框架:

override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) 
    let bounds = UIApplication.shared.keyWindow?.bounds ?? view.bounds
    currentActionSheet?.popoverPresentationController?.sourceRect = CGRect(x: bounds.midX, y: bounds.midY, width: 0, height: 0)

您需要将 UIAlertController 或 popoverController 保存到变量中

weak var currentActionSheet : UIAlertController?

【讨论】:

当我使它变弱并初始化到 logOut() 函数中的 actionSheet 时,actionSheet 保持为零并且不会初始化。此外,当我尝试覆盖 func viewWillTransition 时,我收到一条错误消息,提示“方法不会覆盖其超类中的任何方法”。当我删除覆盖 viewWillTransition 不起作用。我不得不使用这个答案; ***.com/a/42170419/4833705 让它工作。 你不直接初始化那个变量,因为它很弱,你应该给它赋值:currentActionSheet = actionSheet tabBarController?.present(actionSheet, animated: true, completion: nil) 阅读我刚刚发布的答案。如果您发现问题,我会将其删除并接受您的作为已接受的答案。【参考方案2】:

使用@omarzi 答案的一部分,当我将它与SO Answer 结合使用时,我能够让它工作

就像 omarzi 说的,我必须创建一个变量来跟踪操作表:

var actionSheet: UIAlertController?

我使用了上面链接的 SO 答案,就像 omarzi 的建议一样,它建议在设备旋转时更新框架,但答案却是在 viewDidLayoutSubviews() 中更新它,如下所示:

override func viewDidLayoutSubviews() 
    super.viewDidLayoutSubviews()
    
    if let window = UIApplication.shared.windows.first(where:  $0.isKeyWindow ) 
        
        let rect = CGRect(x: window.bounds.midX, y: window.bounds.midY, width: 0, height: 0)
        
        actionSheet?.popoverPresentationController?.sourceRect = rect
    

这与答案是分开的,但响应@omarzi 在下面的评论中所说,完成后将 actionSheet 变量归零:

func presentActionSheet() 
    
    actionSheet = UIAlertController(title: nil, message: "Hello I'm an Action Sheet", preferredStyle: .actionSheet)
    
    let cancelAction = UIAlertAction(title: "Cancel", style: .cancel)  [weak self] (action) in
        
        self?.actionSheet = nil
    
    
    let someAction = UIAlertAction(title: "Do Something", style: .default)  [weak self] (action) in

        // do something

        self?.actionSheet = nil
    
    
    actionSheet?.addAction(someAction)
    actionSheet?.addAction(cancelAction)
    
    present(actionSheet!, animated: true, completion: nil)

【讨论】:

很高兴它对您有所帮助,因为您不会使用弱变量,因此当 UIAlertController 被解除时,您应该将强变量归零 我实际上打算这样做。谢谢您的帮助。我现在正在为你投票。【参考方案3】:

我可以用这个方法解决问题

open override func viewWillLayoutSubviews() 
        super.viewWillLayoutSubviews()
        if let window = UIApplication.shared.keyWindow 
            popoverPresentationController?.sourceRect = CGRect(x: window.bounds.midX, y: window.bounds.midY, width: 0, height: 0)
        

在我的 UIAlertController 扩展中。

【讨论】:

以上是关于当 iPad 旋转时,ActionSheet 的 PopoverController 不会停留在窗口的中心的主要内容,如果未能解决你的问题,请参考以下文章

UIImagePicker 拍照时旋转错误

如何在 iOS13 上的 iPad 上呈现 iPhone 风格的 actionSheet?

swift - 当设备旋转到横向(iPhone/iPad)时,如何让 UIView 变成全屏?

当 iPad 旋转时,UIPopovercontroller 不会保持在其位置

UIAlertController/ActionSheet 在 iPad 上崩溃,如何传递发件人?

当模态视图可见且 iPad 旋转时,UIPopover 的内容会更改为随机方向