UIAlertController have different DOM structure in iPhone and iPad - ios

I am showing UIAlertController in iOS and same code executes for iPhone and iPad. So can anyone help with this. As I google but didn't find any thing to justify this. Below is the code I have used:-
let alert = UIAlertController(title: title, message: message, preferredStyle: UIAlertController.Style.alert)
let okAction = UIAlertAction(title: "Button.Ok".localizedValue(), style: .destructive, handler: { _ in
})
let cancelAction = UIAlertAction(title: "Button.Cancel".localizedValue(), style: .default, handler: { _ in
alert.dismiss(animated: true, completion: nil)
})
alert.accessibilityLabel = self.viewModel.deleteUserConfirmationAccessibilityId()
alert.addAction(okAction)
alert.addAction(cancelAction)
self.present(alert, animated: true, completion: nil)
So the problem is this:-
iPhone : "Remove all saved" text come under UIAView of the DOM Structure when doing automation testing for the UIAlertController in iPhone.
iPad : "Remove all saved" text come under UIView of the DOM Structure when doing automation testing for the UIAlertController in iPad.
So why difference is coming in UIAlertController structure as iPhone iPad showing UIView and iPhone UIAView for the same code.

You have to add popoverpresentation controller for ipad. Please see below code
let alert = UIAlertController(title: title, message: message, preferredStyle: UIAlertController.Style.alert)
let okAction = UIAlertAction(title: "Button.Ok".localizedValue(), style: .destructive, handler: { _ in
})
let cancelAction = UIAlertAction(title: "Button.Cancel".localizedValue(), style: .default, handler: { _ in
alert.dismiss(animated: true, completion: nil)
})
alert.accessibilityLabel =
self.viewModel.deleteUserConfirmationAccessibilityId()
alert.addAction(okAction)
alert.addAction(cancelAction)
//self.present(alert, animated: true, completion: nil)
if(UIDevice.current.userInterfaceIdiom == .pad){
// for iPAD support:
alert.popoverPresentationController?.sourceView = self.view
alert.popoverPresentationController?.sourceRect = CGRect(x:self.view.bounds.width / 2.0, y:150, width:1.0, height:1.0)
self.present(actionSheet, animated: true, completion: nil)
}else{
// for iPHONE support:
let rootViewController: UIViewController = (UIApplication.shared.keyWindow?.rootViewController)!
rootViewController.present(alert, animated: true, completion: nil)
}

Related

How to make UIAlertController compatible for iPad as well as iPhones using Swift

I have created a Image Picker UI where I want to do selection when user clicked on the button,
I have done it using UIAlertController and when I test it in iPhone then it works perfect but,
when I test it in iPad then after button Clicked, the app crashed.
How do I make the UIAlertController compatible for iPad also?
Extension for UIViewController
extension UIViewController{
public func addAlertForiPad(alert: UIAlertController) {
if let alertController = alert.popoverPresentationController {
alertController.sourceView = view
alertController.sourceRect = CGRect(x: view.bounds.midX, y: view.bounds.midY, width: 0, height: 0)
alertController.permittedArrowDirections = []
}
}
}
Usages
let alertController = UIAlertController(title: "Title", message: "Message", preferredStyle: .actionSheet)
if UIDevice.current.userInterfaceIdiom == .pad {
addAlertForiPad(alert: alertController)
}
alertController.popoverPresentationController?.sourceView = view
alertController.addAction(UIAlertAction(title: "Approve", style: .default , handler:{ (UIAlertAction)in
print("User click Approve button")
}))
alertController.addAction(UIAlertAction(title: "Dismiss", style: .cancel, handler:{ (UIAlertAction)in
print("User click Dismiss button")
}))
present(alertController, animated: true, completion: nil)
On an iPad a uialertcontroller must be shown relative to some other controller because you can’t just show it “anywhere on the screen”.
We can use
let ac = UIAlertController(title: nil, message: nil, preferredStyle: UIDevice.current.userInterfaceIdiom == .pad ? .alert : .actionSheet)
Full Code in swift
#IBAction func pickBTN(_ sender: UIButton) {
let ac = UIAlertController(title: nil, message: nil, preferredStyle: UIDevice.current.userInterfaceIdiom == .pad ? .alert : .actionSheet)
ac.addAction(UIAlertAction(title: "Gallery", style: .default, handler: { action in
self.gallery()
}))
ac.addAction(UIAlertAction(title: "Camera", style: .default, handler: { action in
self.camera()
}))
ac.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: { action in
}))
present(ac, animated: true)
}
Functions that are used in above code (for camera & Gallery)
func gallery (){
imagePicker.allowsEditing = false
imagePicker.sourceType = .savedPhotosAlbum
present(imagePicker, animated: true, completion: nil)
}
func camera (){
imagePicker.allowsEditing = false
imagePicker.sourceType = .camera
present(imagePicker, animated: true, completion: nil)
}

UIAlertController: How to make the right button bolded?

When I run the following code, the Yes button is always on the left side, how can I change it to right side and keep it bolded?
func showInAppPurchaseAlert() {
let alertController = UIAlertController.init(title: "Upgrade?", message: "Do you want to upgrade to pro version?", preferredStyle: .alert)
alertController.addAction(UIAlertAction.init(title: "No", style: .default, handler: { action in
self.dismiss(animated: true, completion: nil)
}))
let actionUpgrade = UIAlertAction.init(title: "Yes", style: .cancel, handler: { action in
self.upgradeToPro()
})
alertController.addAction(actionUpgrade)
alertController.preferredAction = actionUpgrade
self.present(alertController, animated: true, completion: nil)
}
The .default style will always have the right side, and the .cancel style the bold text. In order for you to change that, you have to set the preferredAction like you did in your code, but the problem was that you were using .cancel on your "Yes" action, which you wanted on the right side and your "No" action to .default. I tried your code, and this version works for me with your requirements.
func showInAppPurchaseAlert() {
let alertController = UIAlertController.init(title: "Upgrade?", message: "Do you want to upgrade to pro version?", preferredStyle: .alert)
alertController.addAction(UIAlertAction.init(title: "No", style: .cancel, handler: { action in
self.dismiss(animated: true, completion: nil)
}))
let actionUpgrade = UIAlertAction.init(title: "Yes", style: .default, handler: { action in
self.upgradeToPro()
})
alertController.addAction(actionUpgrade)
alertController.preferredAction = actionUpgrade
self.present(alertController, animated: true, completion: nil)
}
Pass .default for style:, it will use the cancel button look and feel.

Use of unresolved identifier 'NSAlert' Swift 4

Can't I use NSAlert in a SKScene ?
let alert = NSAlert()
alert.addButtonWithTitle("Close")
alert.messageText = "Game over"
alert.informativeText = "You won!"
alert.runModal()
ERROR : unresolved identifier 'NSAlert' Swift 4
NSAlert is an OS X API. Since you are on iOS, use UIAlertController:
let alert = UIAlertController(title: "My Alert", message: "This is an alert.", preferredStyle: .alert)
let okAction = UIAlertAction(title: "OK", style: .default) { _ in print("You clicked OK") }
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel) { _ in print("You clicked Cancel") }
alert.addAction(okAction)
alert.addAction(cancelAction)
self.present(alert, animated: true, completion: nil)
If you are inside an SKView, try this:
if let vc = self.window?.rootViewController {
vc.present(alert, animated: true, completion: nil)
}

Trying to present 2 UIAlertControllers back to back

I have a UIAlertController in which the options are populated from an array and are presented to the user. The user then selects an option from the alert. After this, I have a separate alert that provides the user with a confirmation message that has an okay button.
myAlert.addAction(UIAlertAction.init(title: item, style: .Default, handler: {
(UIAlertAction) in
self.chosenBusiness.append(businessNameData[item]!)
}))
self.presentViewController(myAlert, animated: true, completion: nil)
The code above gathers the data from the array and pushes it into actions in myAlert. The code above is inside of a for loop.
After this I use a function to retrieve the topmost view controller, and then push the next alert.
let top = topMostController()
let alertController = UIAlertController(title: "Location pinned", message: "You've successfully pinned this location, good work!", preferredStyle: UIAlertControllerStyle.Alert)
let okAction = UIAlertAction(title: "OK", style: UIAlertActionStyle.Default) {
(result : UIAlertAction) -> Void in
print("OK")
}
alertController.addAction(okAction)
self.presentViewController(myAlert, animated: true, completion: nil)
top.presentViewController(alertController, animated: true, completion: {
_ in
})
The error I receive is:
Attempting to load the view of a view controller while it is
deallocating and is not allowed and may result in undefined behavior.
UIAlertController: 0x1535b1cd0.
Can someone help me with this?
I think this is what you are looking for. The second must be called with the dismissal action of the first. Also, anytime you work with UI, It is safer to use dispatch_async(dispatch_get_main_queue()) {
\\code }
than not if you are not positive you are currently on the main queue.
let firstAlertController = UIAlertController(title: "First", message: "This is the first message.", preferredStyle: UIAlertControllerStyle.Alert)
let secondAlertController = UIAlertController(title: "Second", message: "This is the second message.", preferredStyle: UIAlertControllerStyle.Alert)
let secondDismissAction = UIAlertAction(title: "Dismiss", style: UIAlertActionStyle.Default, completion: nil)
secondAlertController.addAction(secondDismissAction)
let firstDismissAction = UIAlertAction(title: "Dismiss", style: UIAlertActionStyle.Default) {
UIAlertAction in
dispatch_async(dispatch_get_main_queue()) {
self.presentViewController(secondAlertController, animated: true, handler: nil)
}
}
firstAlertController.addAction(firstDismissAction)
self.presentViewController(firstAlertController, animated: true, completion: nil)

UIActionSheet crashes on iPad swift

I have the following button action in a toolbar:
#IBAction func share(sender: AnyObject) {
let modifiedURL1 = "http://www.declassifiedandratified.com/search.html?q=\(self.searchBar.text)"
let modifiedURL = modifiedURL1.stringByReplacingOccurrencesOfString(" ", withString: "%20", options: NSStringCompareOptions.LiteralSearch, range: nil)
let alert = UIAlertController(title: "Share", message: "Share your findings", preferredStyle: UIAlertControllerStyle.ActionSheet)
let twBtn = UIAlertAction(title: "Twitter", style: UIAlertActionStyle.Default) { (alert) -> Void in
if SLComposeViewController.isAvailableForServiceType(SLServiceTypeTwitter){
var twitterSheet:SLComposeViewController = SLComposeViewController(forServiceType: SLServiceTypeTwitter)
twitterSheet.setInitialText("Look what I found on Declassified and Ratified: \(modifiedURL)")
self.presentViewController(twitterSheet, animated: true, completion: nil)
} else {
var alert = UIAlertController(title: "Accounts", message: "Please login to a Twitter account to share.", preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.Default, handler: nil))
self.presentViewController(alert, animated: true, completion: nil)
}
}
let fbBtn = UIAlertAction(title: "Facebook", style: UIAlertActionStyle.Default) { (alert) -> Void in
if SLComposeViewController.isAvailableForServiceType(SLServiceTypeFacebook){
var facebookSheet:SLComposeViewController = SLComposeViewController(forServiceType: SLServiceTypeFacebook)
facebookSheet.setInitialText("Look what I found on Declassified and Ratified: \(modifiedURL)")
self.presentViewController(facebookSheet, animated: true, completion: nil)
} else {
var alert = UIAlertController(title: "Accounts", message: "Please login to a Facebook account to share.", preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.Default, handler: nil))
self.presentViewController(alert, animated: true, completion: nil)
}
}
let safariBtn = UIAlertAction(title: "Open in Safari", style: UIAlertActionStyle.Default) { (alert) -> Void in
let URL = NSURL(string: modifiedURL)
UIApplication.sharedApplication().openURL(URL!)
}
let cancelButton = UIAlertAction(title: "Cancel", style: UIAlertActionStyle.Cancel) { (alert) -> Void in
println("Cancel Pressed")
}
let textBtn = UIAlertAction(title: "Message", style: UIAlertActionStyle.Default) { (alert) -> Void in
if (MFMessageComposeViewController.canSendText()) {
let controller = MFMessageComposeViewController()
controller.body = "Look what I found on Declassified and Ratified: \(modifiedURL)"
controller.messageComposeDelegate = self
self.presentViewController(controller, animated: true, completion: nil)
}
}
alert.addAction(twBtn)
alert.addAction(fbBtn)
alert.addAction(safariBtn)
alert.addAction(textBtn)
alert.addAction(cancelButton)
self.presentViewController(alert, animated: true, completion: nil)
}
However, this code crashes when called on an iPad with a sigbart crash (that is all I can see in the console). I see this is a common problem but the other solutions have not worked for me. I even set the version to the latest iOS and that didn't fix it. Can someone explain?
On an iPad, an action sheet is a popover. Therefore you must give its UIPopoverPresentationController a sourceView and sourceRect, or barButtonItem, so that it has something to attach its arrow to.

Resources