SwiftDeveloper
SwiftDeveloper

Reputation: 7370

How to rotate UIAlertController in Swift

I have a working UIAlertController, but I want to rotate the alert.view by 90 degrees left.

How can I do it? My code is here below:

let alert = UIAlertController(title: "", message: "Message Sample", preferredStyle: .Alert)
alert.addAction(UIAlertAction(title: "Okay", style: .Default){(action)->() in })
presentViewController(alert, animated: true) {}

I tried to add:

 alert.view.transform = CGAffineTransformMakeRotation(CGFloat(M_PI_2))

but it doesn't work.

Thank you !

Upvotes: 7

Views: 2948

Answers (4)

BradB
BradB

Reputation: 509

I was inspired by Nico Nierenberg's answer, but augmented it with some Core Animation logic. This additional logic is necessary to cause the alert to rotate during its presentation and dismissal animation (Nico's original implementation just hides the alert, which would give the same appearance as passing false for animated).

I also added a notification listener which adjusts the rotation when the device orientation changes.

This implementation assumes your original presentation is in portrait only. If your presenting controller can be in other orientations, you'll want to fix the radians method.

Here's the whole class:

import UIKit

class RotatingAlertController : UIAlertController {
    
    private static let animationKey = "rotationHack"
    private static let animationDuration: TimeInterval = 0.3
    private var currentOrientation: UIDeviceOrientation = .portrait
    
    private func radians(for orientation: UIDeviceOrientation) -> CGFloat {
        switch orientation {
        case .landscapeLeft: return CGFloat.pi*0.5
        case .portraitUpsideDown: return CGFloat.pi*1
        case .landscapeRight: return CGFloat.pi*1.5
        default: return 0
        }
    }
    
    private func applyCoreAnimationRotation() {
        let rotationValue = radians(for: currentOrientation)
        let anim = CABasicAnimation(keyPath: "transform.rotation.z")
        anim.duration = 9999
        anim.toValue = rotationValue
        anim.fromValue = rotationValue
        view.layer.add(anim, forKey: RotatingAlertController.animationKey)
    }
    
    private func animatedApplyDeviceRotation() {
        let deviceOrientation = UIDevice.current.orientation
        guard deviceOrientation != currentOrientation &&
              deviceOrientation != .unknown &&
              deviceOrientation != .faceDown &&
              deviceOrientation != .faceUp
        else {
            return
        }
        UIView.animate(withDuration: RotatingAlertController.animationDuration, delay: 0) {
            self.view.transform = CGAffineTransform(rotationAngle: self.radians(for: deviceOrientation))
        }
        currentOrientation = deviceOrientation
    }
    
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        currentOrientation = UIDevice.current.orientation
        applyCoreAnimationRotation()
    }
    
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        view.layer.removeAnimation(forKey: RotatingAlertController.animationKey)
        self.view.transform = CGAffineTransform(rotationAngle: self.radians(for: currentOrientation))
        animatedApplyDeviceRotation()
        NotificationCenter.default.addObserver(forName: UIDevice.orientationDidChangeNotification,
                                               object: nil,
                                               queue: nil) { [weak self] _ in
            self?.animatedApplyDeviceRotation()
        }
    }
    
    override func viewWillDisappear(_ animated: Bool) {
        NotificationCenter.default.removeObserver(self)
        applyCoreAnimationRotation()
    }
}

You can invoke it like this:

let alert = RotatingAlertController(title: "Title", message: "Message", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default))
present(alert, animated: true)

Upvotes: 0

Nupur Parekh
Nupur Parekh

Reputation: 54

If your application supports landscapeRight, landscapeLeft and default (as portrait), you can try this

self.present(alertController, animated: false, completion: {
            switch UIDevice.current.orientation {
            case .landscapeRight:
                self.alertController.view.transform=CGAffineTransform(rotationAngle: CGFloat(-Double.pi / 2))
            case .landscapeLeft:
                self.alertController.view.transform=CGAffineTransform(rotationAngle: CGFloat(Double.pi / 2))
            default:
                self.alertController.view.transform=CGAffineTransform.identity
            }
        })

This will rotate your alert view as per your screen rotation without making any UI animation issue.

Upvotes: 0

Nico Nierenberg
Nico Nierenberg

Reputation: 41

I think this may come up more often with FaceID. I had to solve this problem because I have an app that is in landscape mode, but when the user starts the app on an iPhone or when they have to authenticate to the app with FaceID they are typically in portrait. So I wanted to display alerts based on how the user was holding the phone. My solution was to extend the UIAlertController class as follows;

extension UIAlertController {
    open override func viewWillAppear(_ animated: Bool) {
        view.isHidden = true
    }
    override open func viewDidAppear(_ animated: Bool) {
        let appDirection = UIApplication.shared.statusBarOrientation
        let deviceDirection = UIDevice.current.orientation
        var direction:CGFloat = 0
        if deviceDirection == .portrait {
            if appDirection == .landscapeLeft {
                direction = 1.0
            } else if appDirection == .landscapeRight {
                direction = -1.0
            }
        } else if deviceDirection == .portraitUpsideDown {
            if deviceDirection == .landscapeLeft {
                direction = -1.0
            } else if appDirection == .landscapeRight {
                direction = 1.0
            }
        }
        if direction != 0 {
           view.transform = CGAffineTransform(rotationAngle: CGFloat(Double.pi / 2) * direction);
        }
        view.isHidden = false
    }
    open override func viewWillDisappear(_ animated: Bool) {
        view.isHidden = true
    }
}

Upvotes: 2

Anbu.Karthik
Anbu.Karthik

Reputation: 82759

With this code:

let alert = UIAlertController(title: "", message: "Message Sample", preferredStyle: .Alert)
alert.addAction(UIAlertAction(title: "Okay", style: .Default){(action)->() in })

self.presentViewController(alert, animated: true, completion: {() -> Void in
      alert.view.transform = CGAffineTransformMakeRotation(CGFloat(M_PI_2))

})

Swift3

let alert = UIAlertController(title: "", message: "Message Sample", preferredStyle: .alert)
    alert.addAction(UIAlertAction(title: "Okay", style: .default){(action)->() in })

    self.present(alert, animated: true, completion: {() -> Void in
        alert.view.transform = CGAffineTransform(rotationAngle: CGFloat(Double.pi/2) )

    })

You can achieve this:

enter image description here

Updated answer

 self.presentViewController(alert, animated: true, completion: {() -> Void in
         // alert.view.transform = CGAffineTransformMakeRotation(CGFloat(M_PI_2))

        UIView.animateWithDuration(1.0, delay: 0, options: .CurveLinear, animations: { () -> Void in
            alert.view.transform = CGAffineTransformMakeRotation(CGFloat(M_PI_2))
        }) { (finished) -> Void in
          // do something if you need
        }

    })

Upvotes: 11

Related Questions