0

I have a logOut button that presents an action sheet that centers the popover on an iPad directly in the middle of the window using this code:

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

Here it's presented in the middle of the window:

Center

But when I rotate the iPad it positions itself towards the lower left hand corner:

Lower left hand corner

Why is it not staying in the center of the window whenever I rotate the iPad? How do i fix it so that it stays center on rotation?

ActionSheet:

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

Subclass of 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
        }
    }
}
picciano
  • 22,341
  • 9
  • 69
  • 82
Lance Samaria
  • 17,576
  • 18
  • 108
  • 256

3 Answers3

2

You should update the frame when the device is rotated:

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

You will need to save the UIAlertController or the popoverController to a variable

weak var currentActionSheet : UIAlertController?
omarzl
  • 589
  • 4
  • 9
  • when I make it weak and I initialize to the the actionSheet in the logOut() function the actionSheet stays nil and won't initialize. Also when I tried override func viewWillTransition I get an error that says "Method does not override any method from its superclass". When i remove override viewWillTransition doesn't work. I had to use this answer; https://stackoverflow.com/a/42170419/4833705 to get it to work. – Lance Samaria May 04 '18 at 18:12
  • You don't initialize that variable directly because it is weak, you should assign it: currentActionSheet = actionSheet tabBarController?.present(actionSheet, animated: true, completion: nil) – omarzl May 04 '18 at 18:23
  • read the answer I just posted. If you find the problem I will delete it and accept yours as the accepted answer. – Lance Samaria May 04 '18 at 18:24
1

Using part of @omarzi answer I was able to get it to work when combing it with this SO Answer

Like omarzi said I had to create a variable to keep track of the action sheet:

var actionSheet: UIAlertController?

I used the SO Answer I linked to above which like omarzi’s suggestion it suggested to update the frame when the device is rotated but instead the answer said to update it in viewDidLayoutSubviews() like so:

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
    }
}

This is separate from the answer but in response to what @omarzi said in the comment below to nil out the actionSheet variable when done:

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)
}
Lance Samaria
  • 17,576
  • 18
  • 108
  • 256
0

I was able to solve the issue using this method

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

in my UIAlertController extension.

Paolo Musolino
  • 614
  • 7
  • 9