UIAlertController not working in startup view in swift 3 - ios

I cannot seem to get the alert view to popup at startup of my view. Code is below.
import UIKit
class StartController: UIViewController
{
override func viewDidLoad()
{
super.viewDidLoad()
self.view.backgroundColor = UIColor.white;
startTest();
}
func startTest()
{
let alerta = UIAlertController(title: "Invalid Test", message: "Testing alert controller", preferredStyle: UIAlertControllerStyle.alert);
alerta.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: nil));
self.present(alerta, animated: true, completion: nil);
}
}

the issue is that in viewDidLoad the view hierarchy is not fully set. If you use viewDidAppear, then the hierarchy is set.
If you really want to call this alert in viewDidLoad you can do so by wrapping your presentation call in this GCD block to cause a slight delay
DispatchQueue.main.async {
// Run UI Updates or call completion block
startTest()
}
or use in
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
startTest()
}

Call the startTest() in viewDidAppear method. It works for me.
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
startTest()
}
func startTest()
{
let alerta = UIAlertController(title: "Invalid Test", message: "Testing alert controller", preferredStyle: UIAlertControllerStyle.alert);
alerta.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: nil));
self.present(alerta, animated: true, completion: nil);
}

Just try to put it on viewDidAppear: method
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
startTest()
}

Related

Forcing user to select a row in tableview before navigating

I want to ensure user selects a row from a list of rows (row of schedules) displayed in a tableview controller before navigating to another controller. So in the didSelectRow method, I set a boolean variable scheduleSelected to true. In my viewWillDisappear, I check on scheduleSelected and it it is false then I raise an alert and reload the tableview so I stay on the same tableview instead of navigating. It is not working it navigates to another controller anyways but does raise an alert which is too late.
How can I force the user to select a row before navigating out of the current tableview controller?
May be there is easier way instead of this cumbersome procedure. Please let me know.
override func viewWillDisappear(_ animated: Bool) {
if (scheduleSelected == false ) {
let alertController = UIAlertController(title: "UIAlertController", message: "Select Row", preferredStyle: .alert)
alertController.message = "Choose a Schedule"
alertController.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil))
present(alertController, animated: true, completion: nil)
self.tableView.reloadData()
}else {
let viewController = storyboard?.instantiateViewController(withIdentifier: "Profiles" )
UIApplication.shared.keyWindow?.rootViewController?.navigationController
let tabController = UIApplication.shared.keyWindow?.rootViewController as! ViewTabBarController
let navController = tabController.selectedViewController as! UINavigationController
navController.popToViewController(viewController!, animated: true)
}
}
How you are navigating out of the current tableview controller using button action or using segue.
If you are using segue, then you have to handle this code like below:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if (scheduleSelected == false ) {
let alertController = UIAlertController(title: "UIAlertController", message: "Select Row", preferredStyle: .alert)
alertController.message = "Choose a Schedule"
alertController.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil))
present(alertController, animated: true, completion: nil)
self.tableView.reloadData()
}
}

Get notified when UIAlertController is being dismissed on button press

I would like to do some actions and present some UI right before and right after any UIAlertController dismisses itself (animation is finished) due to user tapping one of the alert's buttons.
How can I get notified that user pressed some button in my UIAlertController and it is going to be dismissed and then is dismissed?
In docs it is advised against subclassing UIAlertController. I still have tried my luck subclassing, thinking that maybe it internally calls func dismiss(animated flag: Bool, completion: (() -> Void)? = nil) on itself. Something like self.dismiss(..., but it doesn't seem to be the case on iOS10.
I have also tried to add 'manual' dismissing into UIAlertAction handler:
let alert = UIAlertController.init(...
let defaultAction = UIAlertAction(title: "OK", style: .default, handler: { action in
alert.dismiss(animated: true, completion: {
print("Dismissed")
})
})
alert.addAction(defaultAction)
But it seems that alert is dismissed after button press but before calling handler. Anyhow it doesn't work as well. Even if it worked it would be a bit bothersome to remember to add my code into each and every UIAlertAction handler.
I would appreciate any ideas.
although subclassing is not advised you could use a simple subclass like this:
class CustomAlertController: UIAlertController {
var willDisappearBlock: ((UIAlertController) -> Void)?
var didDisappearBlock: ((UIAlertController) -> Void)?
override func viewWillDisappear(_ animated: Bool) {
willDisappearBlock?(self)
super.viewWillDisappear(animated)
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
didDisappearBlock?(self)
}
}
you could then use it like this:
let alert = CustomAlertController(title: "Alert", message: "This is an alert. Press Yes or No.", preferredStyle: .alert)
alert.willDisappearBlock = { alert in
print("\(alert) will disappear")
}
alert.didDisappearBlock = { alert in
print("\(alert) did disappear")
}
alert.addAction(UIAlertAction(title: "Yes", style: .default, handler: { (yesAction) in
print("User tapped Yes.")
}))
alert.addAction(UIAlertAction(title: "No", style: .cancel, handler: { (yesAction) in
print("User tapped No.")
}))
present(alert, animated: true) {
print("presentCompletion")
}
output is in the following order:
presentCompletion
will disappear
did
disappear
User tapped Yes.
You can disable the closing animation altogether like this:
class InstantCloseAlertController: UIAlertController {
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
UIView.setAnimationsEnabled(false)
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
UIView.setAnimationsEnabled(true)
}
}
This will trigger the action handler instantly.
But I'm also currently working on exactly what you're asking (keeping the animation). I got it all sorted out but needs some more work. It involves lot's of hacking, haha. I'll post it when it's done.

UIAlertController, eliminate the animation on closing?

Here's a simple action sheet,
let choice = UIAlertController(title: "Choose", message: nil, preferredStyle: .actionSheet)
choice.addAction(UIAlertAction(title: "Camera", style: .default, handler: { _ in
self.happyCamera() }))
choice.addAction(UIAlertAction(title: "Album", style: .default, handler: { _ in
self.happyAlbum() }))
choice.addAction(UIAlertAction.init(title: "Cancel", style: .cancel, handler: nil))
somewhere?.present(choice, animated: false, completion: nil)
When the action sheet appears (note that present#animated is false) it just clicks on to the screen, no cheesey animation.
However, when the user taps one of the three choices, or, taps "off", the action sheet leaves the screen by using the cheesey animation.
(In 10.3 specifically, it slides downwards off the screen.)
Is there a way to turn off that exit animation?
If you subclass UIAlertController...it doesn't work?
As DS suggests below, you could subclass UIAlertController.
However - strangely - it does nothing. Here's a test
func _test() {
let msg = SuperiorUIAlertController(
title: "Hello", message: "Hello",
preferredStyle: UIAlertControllerStyle.alert)
msg.addAction(UIAlertAction(
title: "OK", style: UIAlertActionStyle.default,
handler: nil))
let win = UIWindow(frame: UIScreen.main.bounds)
let vc = UIViewController()
vc.view.backgroundColor = .clear
win.rootViewController = vc
win.windowLevel = UIWindowLevelAlert + 1
win.makeKeyAndVisible()
vc.present(msg, animated: false, completion: nil)
}
class SuperiorUIAlertController: UIAlertController {
override func dismiss(animated flag: Bool, completion: (() -> Void)? = nil) {
print("You should see this! \(flag)")
self.dismiss(animated: false, completion: completion)
}
}
Indeed, the text "You should see this" never appears.
I hate to answer my own question, but as of late 2017, there is no way. Weird right?
Hope this fact helps someone.
The one more way you can Override dismiss method of viewController. If you don't wanna override other animations check animated flag value or make a flag in below method.
Make your AlertController globally
var choice = UIAlertController()
Make sure add this method in your viewController which you are presented alert
Dismiss presented alert without animation like below
override func dismiss(animated flag: Bool, completion: (() -> Void)? = nil) {
self.choice.dismiss(animated: false, completion: nil)
}
Try subclassing UIAlertController like this:
class InstantCloseAlertController: UIAlertController {
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
UIView.setAnimationsEnabled(false)
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
UIView.setAnimationsEnabled(true)
}
}

Trouble getting alert to pop up in swift

This function is called in my viewDidLoad. It doesn't get an error but nothing ever happens. It is definitely getting called though because I told it to print and it worked.
Here is the code for alert:
func makeAlert()
{
let alertController = UIAlertController(title: "Title", message: "Message", preferredStyle: UIAlertControllerStyle.Alert)
// Create the actions
let okAction = UIAlertAction(title: "OK", style: UIAlertActionStyle.Default) {
UIAlertAction in
NSLog("OK Pressed")
}
let cancelAction = UIAlertAction(title: "Cancel", style: UIAlertActionStyle.Cancel) {
UIAlertAction in
NSLog("Cancel Pressed")
}
// Add the actions
alertController.addAction(okAction)
alertController.addAction(cancelAction)
// Present the controller
self.presentViewController(alertController, animated: true, completion: nil)
The problem here is that you are trying to display your alertController before the presenting view controller (the one with your viewDidLoad) is displayed on screen. viewDidLoad() is called after your view controller is loaded into memory, not necessarily when its view is in the view hierarchy.
Therefore, call makeAlert() in viewDidAppear(_:):
override func viewDidAppear(animated: Bool) {
makeAlert()
}
This ensures that your view controller is already displayed and is able to present your alertController.
Reading about viewDidLoad() and viewDidAppear(_:) here is helpful:
https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIViewController_Class/index.html#//apple_ref/occ/instm/UIViewController/

Issue with ImagePicker Controller UIBarButtonItem instad of UIButton

I want to add uiimagepickercontroller to a VC but I chnage the button to bar button then
I got an error after change the btnClickMe for UIButton to UIBarButtonItem I didn't understand the Error what seems the problem ?
value type UIBarButtonItem has no member 'Frame'
I deleted the frame but it didn't works
class ViewController: UIViewController,UIAlertViewDelegate,UIImagePickerControllerDelegate,UINavigationControllerDelegate,UIPopoverControllerDelegate
{
#IBOutlet weak var btnClickMe: UIButton!
#IBOutlet weak var imageView: UIImageView!
var picker:UIImagePickerController?=UIImagePickerController()
var popover:UIPopoverController?=nil
override func viewDidLoad()
{
super.viewDidLoad()
picker!.delegate=self
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func btnImagePickerClicked(sender: AnyObject)
{
let alert:UIAlertController=UIAlertController(title: "Choose Image", message: nil, preferredStyle: UIAlertControllerStyle.ActionSheet)
let cameraAction = UIAlertAction(title: "Camera", style: UIAlertActionStyle.Default)
{
UIAlertAction in
self.openCamera()
}
let gallaryAction = UIAlertAction(title: "Gallary", style: UIAlertActionStyle.Default)
{
UIAlertAction in
self.openGallary()
}
let cancelAction = UIAlertAction(title: "Cancel", style: UIAlertActionStyle.Cancel)
{
UIAlertAction in
}
// Add the actions
picker?.delegate = self
alert.addAction(cameraAction)
alert.addAction(gallaryAction)
alert.addAction(cancelAction)
// Present the controller
if UIDevice.currentDevice().userInterfaceIdiom == .Phone
{
self.presentViewController(alert, animated: true, completion: nil)
}
else
{
popover=UIPopoverController(contentViewController: alert)
popover!.presentPopoverFromRect(btnClickMe.frame, inView: self.view, permittedArrowDirections: UIPopoverArrowDirection.Any, animated: true)
}
}
func openCamera()
{
if(UIImagePickerController .isSourceTypeAvailable(UIImagePickerControllerSourceType.Camera))
{
picker!.sourceType = UIImagePickerControllerSourceType.Camera
self .presentViewController(picker!, animated: true, completion: nil)
}
else
{
openGallary()
}
}
func openGallary()
{
picker!.sourceType = UIImagePickerControllerSourceType.PhotoLibrary
if UIDevice.currentDevice().userInterfaceIdiom == .Phone
{
self.presentViewController(picker!, animated: true, completion: nil)
}
else
{
popover=UIPopoverController(contentViewController: picker!)
popover!.presentPopoverFromRect(btnClickMe.frame, inView: self.view, permittedArrowDirections: UIPopoverArrowDirection.Any, animated: true)
}
}
func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : AnyObject])
{
picker .dismissViewControllerAnimated(true, completion: nil)
imageView.image=info[UIImagePickerControllerOriginalImage] as? UIImage
}
func imagePickerControllerDidCancel(picker: UIImagePickerController)
{
print("picker cancel.")
}
}
UIPopoverController has been deprecated in iOS 9. Xcode probably shows this warning:
'UIPopoverController' was deprecated in iOS 9.0: UIPopoverController is deprecated. Popovers are now implementd as UIViewController presentations. Use a modal presentation style of UIModalPresentationPopover and UIPopoverPresentationController.
However, if you still want to use it, you need to use the presentPopoverFromBarButtonItem method instead of presentPopoverFromRect, as UIBarButtonItems don't have a frame property.

Resources