Swift & SpriteKit - How to present alert view in GameScene - ios

I need help presenting an alert view in the game scene. Im currently struggling to do so as GameScene.Swift isnt a standard ViewController. If it helps I need to do so as I need the user to input a value which is used as a coordinate for the ball Sprite Kit Node in my game. The input is only a standard integer so that isnt an issue. Any other idea of how I can do this which isnt through an alert view is also welcome.
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
let view = self.view as! SKView
if view.scene == nil {
view.showsFPS = false
view.showsNodeCount = false
let gameScene = GameScene(size: view.bounds.size)
gameScene.scaleMode = SKSceneScaleMode.AspectFill
view.presentScene(gameScene)
}
}
That is in the GameViewController file
var vc : GameViewController!
override init(size: CGSize) {
super.init(size: size)
let alertController = UIAlertController(title: "Bll Starting Position", message: "Please Enter a X Coordinate Value IN Range 0 to 345 ", preferredStyle: .Alert)
alertController.addTextFieldWithConfigurationHandler { (textField) in
textField.placeholder = "Value Must Be In Range 0 To 345"
textField.autocapitalizationType = UITextAutocapitalizationType.None
textField.autocorrectionType = UITextAutocorrectionType.No
textField.clearsOnBeginEditing = true
textField.clearsOnInsertion = true
textField.clearButtonMode = UITextFieldViewMode.Always
let cancelBtn = UIAlertAction(title: "Cancel", style: .Cancel, handler: nil)
let confirmBtn = UIAlertAction(title: "Confirm", style: .Default, handler: { (confirmView) in
if let field = alertController.textFields![0] as? UITextField {
}
})
alertController.addAction(confirmBtn)
alertController.addAction(cancelBtn)
self.vc.presentViewController(alertController, animated: true, completion: nil)
}
Thanks

You can show UIAlertControllers directly from SKScenes, simply show them on the rootViewController, which is probably the best place to show them anyway.
view?.window?.rootViewController?.present...
In general its not the best practice to reference the GameViewController in SKScenes and I never actually got to a point where I was forced to do so. NSNotificationCenter, delegation or protocol extensions are the better way.
I actually use a helper for Alerts I made using Swift 2's protocol extensions.
Just make a new .swift file and add this code
import SpriteKit
protocol Alertable { }
extension Alertable where Self: SKScene {
func showAlert(withTitle title: String, message: String) {
let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
let okAction = UIAlertAction(title: "OK", style: .cancel) { _ in }
alertController.addAction(okAction)
view?.window?.rootViewController?.present(alertController, animated: true)
}
func showAlertWithSettings(withTitle title: String, message: String) {
let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
let okAction = UIAlertAction(title: "OK", style: .cancel) { _ in }
alertController.addAction(okAction)
let settingsAction = UIAlertAction(title: "Settings", style: .default) { _ in
guard let url = URL(string: UIApplicationOpenSettingsURLString) else { return }
if #available(iOS 10.0, *) {
UIApplication.shared.open(url)
} else {
UIApplication.shared.openURL(url)
}
}
alertController.addAction(settingsAction)
view?.window?.rootViewController?.present(alertController, animated: true)
}
}
Now in your scenes you need to show alerts you simply conform to the protocol
class GameScene: SKScene, Alertable {
}
and call the methods like
showAlert(withTitle: "Alert title", message: "Alert message")
as if they are part of the scene itself.
Hope this helps

There may be the following options:
1) Quick solution. Do not use UIAlertController, use UIAlertView. Like that:
alert.show()
However, UIAlertView is deprecated so it's not quite safe to rely on it.
2) A better solution. Make your SKScene subclass hold a reference to the view controller which you use to present the scene and when you create the scene assign it the view controller:
myScene.viewController = self
And then you can use it.
self.viewController.presentViewController(alertController, animated: true, completion: nil)

Related

How to create a reusable alert view used across different view controllers?

in the simple language: can we create that alert Box as a reusable method
i want to made 1 Alert box in to the function.
like this.
// this code has separate file
import UIKit
struct AlertView {
public func showAlertBox(title: String, message: String) -> UIAlertController {
let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .cancel, handler: { _ in
}))
return alert
}
}
and here is my caller ViewController file code.
#IBAction func submitPressed(_ sender: Any) {
let alertView = AlertView()
let alert = alertView.showAlertBox(title: "Hours Added", message: "Hours have been updated")
alert.present(alert, animated: true) {
self?.dismiss(animated: true, completion: nil)
self?.timeSubmitted = true
self?.performSegue(withIdentifier: "unwindToMyHours", sender: nil)
}
}
You need alert action to performing ok action.
You can modify your code by this
Here are the helper functions.
struct AlertView {
public static func showAlertBox(title: String, message: String, handler: ((UIAlertAction)->Void)?) -> UIAlertController {
let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .cancel, handler: handler))
return alert
}
}
extension UIAlertController {
func present(on viewController: UIViewController, completion: (() -> Void)? = nil) {
viewController.present(self, animated: true, completion: completion)
}
}
Usage
class ViewController: UIViewController {
#IBAction func submitPressed(_ sender: Any) {
AlertView.showAlertBox(title: "Hours Added", message: "Hours have been updated") { [weak self] action in
// Okay action code
}.present(on: self) { [weak self] in
self?.dismiss(animated: true, completion: nil)
self?.timeSubmitted = true
self?.performSegue(withIdentifier: "unwindToMyHours", sender: nil)
}
}
}
Note: self is dismissing so might be your alert is not presenting. You can present your alert on top most view controller. see this
Yes, you can create a shared alert controller. I would suggest making it a static method of your struct, or even a global function. It's silly to create an instance of your struct only to invoke a method that doesn't need any instance variables:
public static func alertBox(title: String, message: String) -> UIAlertController {
let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .cancel, handler: { _ in
}))
return alert
}
}
And then you'd invoke it by saying
let alert = AlertView.alertBox(title: "title",message: "message" )
(Your function doesn't show the alert, it just creates it. I would therefore suggest naming it alertBox, not 'showAlertBox`.
Yes, you can use a shared alert controller. What I would suggest is making the AlertView struct, a singleton. You can change the struct as follows
struct AlertView {
// Shared instance
static let shared: AlertView = AlertView()
// Private initializer to prevent creating of new instances
private init() {}
public func showAlertBox(title: String, message: String) -> UIAlertController {
let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .cancel, handler: { _ in
}))
return alert
}
}
By doing so, you will be able to create just one instance of AlertView and you have to use that single instance in your program. That way you won't have to create new instances of AlertView every time you need to display an alert. You can invoke it using,
let alert = AlertView.shared.showAlertBox(title: "Hours Added", message: "Hours have been updated")
Edit - You can refer this medium article to understand the singleton design patter
The best way is to perform simple encapsulation through extension, and complex encapsulation just loses applicability
example:
let alertVC = UIAlertController(title: "title", message: "message", preferredStyle: .alert)
.addActionTitles(titles) { (alertVC, action) in
let actionIdx = alertVC.actions.firstIndex(of: action)
DDLog(actionIdx)
}
self.present(alertVC, animated: true, completion:{})
code:
public let kTitleSure = "Yes"
public let kTitleCancell = "No"
/// contentViewController
public let kAlertContentViewController = "contentViewController"
#objc public extension UIAlertController{
///add UIAlertAction
#discardableResult
func addActionTitles(_ titles: [String]? = [kTitleCancell, kTitleSure], handler: ((UIAlertController, UIAlertAction) -> Void)? = nil) -> Self {
titles?.forEach({ (string) in
let style: UIAlertAction.Style = string == kTitleCancell ? .cancel : .default
self.addAction(UIAlertAction(title: string, style: style, handler: { (action) in
handler?(self, action)
}))
})
return self
}
///add textField
#discardableResult
func addTextFieldPlaceholders(_ placeholders: [String]?, handler: ((UITextField) -> Void)? = nil) -> Self {
if self.preferredStyle != .alert {
return self
}
placeholders?.forEach({ (string) in
self.addTextField { (textField: UITextField) in
textField.placeholder = string
handler?(textField)
}
})
return self
}
#discardableResult
func setContent(vc: UIViewController, height: CGFloat) -> Self {
setValue(vc, forKey: kAlertContentViewController)
vc.preferredContentSize.height = height
preferredContentSize.height = height
return self
}
}
github

How to display an Alert from a different class

I created a Utilities class to hold some common functions, one of which is an alertUser function that if called, will display an Alert box to the user with the provided title and message text. In another class file, I am validating some text field entries and if the validation doesn't pass, then I want to use the alertUser function from the Utilities class. However, when I do this, I get the following error message in the Xcode log:
Warning: Attempt to present <UIAlertController: 0x7f9c4be0b140> on <MyAppName.Utilities: 0x7f9c4be1cb60> whose view is not in the window hierarchy!
The calling code is in a UIViewController class file. Here's the code which is in the
class ItemSettingsVC: UIViewController:
private func validateNameField() -> Bool {
var passed = false
if (nameField.hasText) {
passed = true
} else {
Utilities().alertUser(strTitle: "Alert", strMessage: strInvalidNameFieldErrorMsg)
passed = false
}
return passed
}
Here's the alertUser function which is in the
class Utilities: UIViewController:
public func alertUser(strTitle: String, strMessage: String) {
let myAlert = UIAlertController(title: strTitle, message: strMessage, preferredStyle: UIAlertControllerStyle.alert)
let okAction = UIAlertAction(title: "Ok", style: UIAlertActionStyle.default, handler: nil)
myAlert.addAction(okAction)
self.present(myAlert, animated: true, completion: nil)
}
This is running on iOS. I'm using Xcode 8 and swift 3. Any help is greatly appreciated. Thanks.
This should do it:
public func alertUser(strTitle: String, strMessage: String) {
let myAlert = UIAlertController(title: strTitle, message: strMessage, preferredStyle: UIAlertControllerStyle.alert)
let okAction = UIAlertAction(title: "Ok", style: UIAlertActionStyle.default, handler: nil)
myAlert.addAction(okAction)
UIApplication.shared.delegate?.window??.rootViewController?.present(myAlert, animated: true, completion: nil)
}
You have to add an additional parameter in your alertUser function, which would be the VC that will present the alert controller.
for example:
public func alertUser(strTitle: String, strMessage: String, viewController: UIViewController) {
let myAlert = UIAlertController(title: strTitle, message: strMessage, preferredStyle: UIAlertControllerStyle.alert)
let okAction = UIAlertAction(title: "Ok", style: UIAlertActionStyle.default, handler: nil)
myAlert.addAction(okAction)
viewController.present(myAlert, animated: true, completion: nil)
}
But I would recommend that you just make an extension of UIViewController and add your func alertUser()* there because you would surely use this alertUser in different VCs and complexity wise in my opinion, this would be more optimized.
Like this:
extension UIViewController {
func showAlert(title: String, message: String, callback: #escaping () -> ()) {
let alert = UIAlertController(title: title, message: message, preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "Ok", style: .default, handler: {
alertAction in
callback()
}))
self.present(alert, animated: true, completion: nil)
}
//add additional functions here if necessary
//like a function showing alert with cancel
}
NOTE : Please don't make your Utilities class a subclass of UIViewController, it would also be better to make it a struct handling static functions and/or variables
Use this class for easy to show Alert or ActionSheet
UIAlertController Extension
public extension UIAlertController {
public func showAlert(animated: Bool = true, completionHandler: (() -> Void)? = nil) {
guard let rootVC = UIApplication.shared.keyWindow?.rootViewController else {
return
}
var forefrontVC = rootVC
while let presentedVC = forefrontVC.presentedViewController {
forefrontVC = presentedVC
}
forefrontVC.present(self, animated: animated, completion: completionHandler)
}
}
AppAlert Class Create For UIAlertController Show
public class AppAlert {
private var alertController: UIAlertController
public init(title: String? = nil, message: String? = nil, preferredStyle: UIAlertControllerStyle) {
self.alertController = UIAlertController(title: title, message: message, preferredStyle: preferredStyle)
}
public func setTitle(_ title: String) -> Self {
alertController.title = title
return self
}
public func setMessage(_ message: String) -> Self {
alertController.message = message
return self
}
public func setPopoverPresentationProperties(sourceView: UIView? = nil, sourceRect:CGRect? = nil, barButtonItem: UIBarButtonItem? = nil, permittedArrowDirections: UIPopoverArrowDirection? = nil) -> Self {
if let poc = alertController.popoverPresentationController {
if let view = sourceView {
poc.sourceView = view
}
if let rect = sourceRect {
poc.sourceRect = rect
}
if let item = barButtonItem {
poc.barButtonItem = item
}
if let directions = permittedArrowDirections {
poc.permittedArrowDirections = directions
}
}
return self
}
public func addAction(title: String = "", style: UIAlertActionStyle = .default, handler: #escaping ((UIAlertAction!) -> Void) = { _ in }) -> Self {
alertController.addAction(UIAlertAction(title: title, style: style, handler: handler))
return self
}
public func addTextFieldHandler(_ handler: #escaping ((UITextField!) -> Void) = { _ in }) -> Self {
alertController.addTextField(configurationHandler: handler)
return self
}
public func build() -> UIAlertController {
return alertController
}
}
Used For Open AlertBox
AppAlert(title: "Question", message: "Are you sure?", preferredStyle: .alert)
.addAction(title: "NO", style: .cancel) { _ in
// action
}
.addAction(title: "Okay", style: .default) { _ in
// action
}
.build()
.showAlert(animated: true)
Used For ActionSheet Open
if UIDevice.current.userInterfaceIdiom != .pad {
// Sample to show on iPhone
AppAlert(title: "Question", message: "Are you sure?", preferredStyle: .actionSheet)
.addAction(title: "NO", style: .cancel) {_ in
print("No")
}
.addAction(title: "YES", style: .default) { _ in
print("Yes")
}
.build()
.showAlert(animated: true)
} else {
// Sample to show on iPad
AppAlert(title: "Question", message: "Are you sure?", preferredStyle: .actionSheet)
.addAction(title: "Not Sure", style: .default) {
_ in
print("No")
}
.addAction(title: "YES", style: .default) { _ in
print("Yes")
}
.setPopoverPresentationProperties(sourceView: self, sourceRect: CGRect.init(x: 0, y: 0, width: 100, height: 100), barButtonItem: nil, permittedArrowDirections: .any)
.build()
.showAlert(animated: true)
}
First find out the topmost viewController on your window .
Get the top ViewController in iOS Swift
and then present your alert on that viewController.No need to pass any parameter.
public func alertUser(strTitle: String, strMessage: String) {
let myAlert = UIAlertController(title: strTitle, message: strMessage, preferredStyle: UIAlertControllerStyle.alert)
let okAction = UIAlertAction(title: "Ok", style: UIAlertActionStyle.default, handler: nil)
myAlert.addAction(okAction)
topmostVC().present(myAlert, animated: true, completion: nil)
}

How to get UIViewController in a singleton?

I'm using a singleton class to validate a network response. Inside the function (validResponse()) I'm calling in the singleton, I call another function which pops up an alert box, to let the user know there was an error.
The function inside my singleton class:
func validResponse(data: Data?, response: URLResponse?, error: Error?, viewController: UIViewController, context: String?) -> Bool {
...
DispatchQueue.main.async {
AlertHelper.showAlertWrapper(viewController: viewController, alertTitle: "Error", alertMessage: self.genericError)
}
}
The AlertHelper code:
class AlertHelper {
static func showAlertWrapper(viewController: UIViewController, alertTitle: String, alertMessage: String) {
let alertController = UIAlertController(title: alertTitle, message: alertMessage, preferredStyle: .alert);
let okAction = UIAlertAction(title: "OK", style: .default, handler: nil);
alertController.addAction(okAction);
viewController.present(alertController, animated: true, completion: nil);
}
}
Calling validResponse():
let result = self.networkHelper.validResponse(data: data, response: response, error: error, viewController: self, context: "Delete section")
In the above instance self is not going to work, and is just temporarily there until I figure out what to do. I understand I could just pass the relevant UIViewController in viewController, like I did for showAlertWrapper. However this is a bit messy.
Is there some way I can reference the currently present view controller in my singleton class, so that I dont have to pass it in validResponse()?
You may want to get the top most view controller currently showing in your app. You can do this by retrieving the top most view controller when your singleton wants to display an error.
class AlertHelper {
static func showAlertWrapper(alertTitle: String, alertMessage: String) {
let alertController = UIAlertController(title: alertTitle, message: alertMessage, preferredStyle: .alert);
let okAction = UIAlertAction(title: "OK", style: .default, handler: nil);
alertController.addAction(okAction);
if var topController = UIApplication.sharedApplication().keyWindow?.rootViewController {
while let presentedViewController = topController.presentedViewController {
topController = presentedViewController
}
// viewController should now be your topmost view controller
viewController.present(alertController, animated: true, completion: nil);
}
}
}
And call your showAlertWrapper:
AlertHelper.showAlertWrapper(alertTitle: "Error", alertMessage: self.genericError)
A workaround and maybe swiftier way would be to have the showAlertWrapperin the UIViewController:
extension UIViewController {
func showAlertWrapper(title: String, message: String) {
let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert);
let okAction = UIAlertAction(title: "OK", style: .default, handler: nil);
alertController.addAction(okAction);
present(alertController, animated: true, completion: nil);
}
}
Then you would just do
DispatchQueue.main.async {
viewController.showAlertWrapper(title: "Error", message: self.genericError)
}

How to create uialertcontroller in global swift

I'm trying to create uialertcontroller in Config.swift file as follow.
static func showAlertMessage(titleStr:String, messageStr:String) -> Void {
let window : UIWindow?
let alert = UIAlertController(title: titleStr, message: messageStr, preferredStyle: UIAlertControllerStyle.Alert);
self.window!.presentViewController(alert, animated: true, completion: nil)
}
problem is I've found problem in self.window!.
Type 'Config' has no member 'window'
Please let me know how to solve that issue.
swift
Here's what I used, this is the same as #penatheboss answered, just add the ability of adding actions and handlers.
extension UIViewController {
func popupAlert(title: String?, message: String?, actionTitles:[String?], actions:[((UIAlertAction) -> Void)?]) {
let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
for (index, title) in actionTitles.enumerated() {
let action = UIAlertAction(title: title, style: .default, handler: actions[index])
alert.addAction(action)
}
self.present(alert, animated: true, completion: nil)
}
}
Just make sure actionTitles and actions array the same count. Pass nil if you don't need any action handler closure.
self.popupAlert(title: "Title", message: " Oops, xxxx ", actionTitles: ["Option1","Option2","Option3"], actions:[{action1 in
},{action2 in
}, nil])
Objective C:
Add the category for UIViewController
UIViewController+PopAlert.h
#import <UIKit/UIKit.h>
#interface UIViewController (UIViewControllerCategory)
- (void) popAlertWithTitle: (NSString*) title message: (NSString*) message actionTitles:(NSArray *) actionTitles actions:(NSArray*)actions;
#end
UIViewController+PopAlert.m
#import "UIViewController+PopAlert.h"
#implementation UIViewController (UIViewControllerCategory)
- (void) popAlertWithTitle: (NSString*) title message: (NSString*) message actionTitles:(NSArray *) actionTitles actions:(NSArray*)actions {
UIAlertController *alert = [UIAlertController alertControllerWithTitle:title message:message preferredStyle:UIAlertControllerStyleAlert];
[actionTitles enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
UIAlertAction *action = [UIAlertAction actionWithTitle:obj style:UIAlertActionStyleDefault handler:actions[idx]];
[alert addAction:action];
}];
[self presentViewController:alert animated:YES completion:nil];
}
#end
Usage:
#import UIViewController+PopAlert.h
...
[super viewDidDisappear:animated];
NSArray *actions = #[^(){NSLog(#"I am action1");}, ^(){NSLog(#"I am action2");}];
[self popAlertWithTitle:#"I am title" message:#"good" actionTitles:#[#"good1", #"good2"] actions:actions];
self.window would mean that there's a window object in this class, and it's not the case.
You would need to use your let window : UIWindow? with window?.presentViewController(alert, animated: true, completion: nil), but this won't help, since this window does not actually represent any existing window, and it's not a view controller anyway.
So I suggest you pass the actual view controller you'll be using to the method:
static func showAlertMessage(vc: UIViewController, titleStr:String, messageStr:String) -> Void {
let alert = UIAlertController(title: titleStr, message: messageStr, preferredStyle: UIAlertControllerStyle.Alert);
vc.presentViewController(alert, animated: true, completion: nil)
}
and you call it from a class where a UIViewController object is available.
Details
Swift 5.1, Xcode 11.3.1
Global UIAlertController With UIViewController Extension
extension UIViewController{
// Global Alert
// Define Your number of buttons, styles and completion
public func openAlert(title: String,
message: String,
alertStyle:UIAlertController.Style,
actionTitles:[String],
actionStyles:[UIAlertAction.Style],
actions: [((UIAlertAction) -> Void)]){
let alertController = UIAlertController(title: title, message: message, preferredStyle: alertStyle)
for(index, indexTitle) in actionTitles.enumerated(){
let action = UIAlertAction(title: indexTitle, style: actionStyles[index], handler: actions[index])
alertController.addAction(action)
}
self.present(alertController, animated: true)
}
}
Usage
Alert
self.openAlert(title: "alert",
message: "add your message",
alertStyle: .alert,
actionTitles: ["Okay", "Cancel"],
actionStyles: [.default, .cancel],
actions: [
{_ in
print("okay click")
},
{_ in
print("cancel click")
}
])
ActionSheet
self.openAlert(title: "actionsheet",
message: "add your message",
alertStyle: .actionSheet,
actionTitles: ["Okay", "Cancel"],
actionStyles: [.default, .cancel],
actions: [
{_ in
print("okay click")
},
{_ in
print("cancel click")
}
])
I suggest creating an extension:
extension UIViewController {
func showAlertMessage(titleStr:String, messageStr:String) {
let alert = UIAlertController(title: titleStr, message: messageStr, preferredStyle: UIAlertControllerStyle.Alert)
self.presentViewController(alert, animated: true, completion: nil)
}
}
You can try this, please add below code in AppDelegate.swift file.
static func showAlertView(vc : UIViewController, titleString : String , messageString: String) ->()
{
let alertView = UIAlertController(title: titleString, message: messageString, preferredStyle: .alert)
let alertAction = UIAlertAction(title: "ok", style: .cancel) { (alert) in
vc.dismiss(animated: true, completion: nil)
}
alertView.addAction(alertAction)
vc.present(alertView, animated: true, completion: nil)
}
and call showAlertView method from any viewcontroller
AppDelegate.showAlertView(vc: self, titleString: "testTitle", messageString: "test msg")
I am suggest you write this code, but if you really need, try this:
static func showAlertMessage(titleStr:String, messageStr:String) -> Void {
let alert = UIAlertController(title: titleStr, message: messageStr, preferredStyle: UIAlertControllerStyle.Alert);
if let viewController = UIApplication.sharedApplication().windows.first?.rootViewController as UIViewController? {
viewController.presentViewController(alert, animated: true, completion: nil)
}
}
At least it won't break down.
#Eric D better answer.
I created a alerMessage class .I can call any where in my application
//Common Alert Message Class
class AlertMessage {
internal static var alertMessageController:UIAlertController!
internal static func disPlayAlertMessage(titleMessage:String, alertMsg:String){
AlertMessage.alertMessageController = UIAlertController(title: titleMessage, message:
alertMsg, preferredStyle: UIAlertControllerStyle.Alert)
AlertMessage.alertMessageController.addAction(UIAlertAction(title: "Ok", style: UIAlertActionStyle.Default,handler: nil))
if let controller = UIApplication.sharedApplication().keyWindow?.rootViewController?.presentedViewController {
controller.presentViewController(AlertMessage.alertMessageController, animated: true, completion: nil)
}
else{
UIApplication.sharedApplication().delegate?.window!!.rootViewController?.presentViewController(AlertMessage.alertMessageController, animated: true, completion: nil)
}
return
}
}
Please refer the below GIT Example
https://github.com/amilaim/CommonAlertView
// ViewController.swift
// CommonAlertView
//
// Created by Amila Munasinghe on 4/25/17.
// Copyright © 2017 Developer Insight. All rights reserved.
//
import UIKit
class ViewController: UIViewController,AlertViewControllerDelegate {
#IBOutlet weak var AlertViewResultTextOutlet: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
#IBAction func ShowAlertAction(_ sender: Any) {
let alert = AlertViewController.sharedInstance
alert.delegate = self
alert.SubmitAlertView(viewController: self,title: "Developer Insight", message: "Please enter any text value")
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func SubmitAlertViewResult(textValue : String) {
AlertViewResultTextOutlet.text = textValue
}
}
Common UIAlertViewController Implementation
import UIKit
protocol AlertViewControllerDelegate {
func SubmitAlertViewResult(textValue : String)
}
class AlertViewController {
static let sharedInstance = AlertViewController()
private init(){}
var delegate : AlertViewControllerDelegate?
func SubmitAlertView(viewController : UIViewController,title : String, message : String){
let alert = UIAlertController(title: title,
message: message,
preferredStyle: .alert)
// Submit button
let submitAction = UIAlertAction(title: "Submit", style: .default, handler: { (action) -> Void in
// Get 1st TextField's text
let textField = alert.textFields![0]
if(textField.text != "")
{
self.delegate?.SubmitAlertViewResult(textValue: textField.text!)
}
})
// Cancel button
let cancel = UIAlertAction(title: "Cancel", style: .destructive, handler: { (action) -> Void in })
// Add 1 textField and cutomize it
alert.addTextField { (textField: UITextField) in
textField.keyboardAppearance = .dark
textField.keyboardType = .default
textField.autocorrectionType = .default
textField.placeholder = "enter any text value"
textField.clearButtonMode = .whileEditing
}
// Add action buttons and present the Alert
alert.addAction(submitAction)
alert.addAction(cancel)
viewController.present(alert, animated: true, completion: nil)
}
}
What I'm using based on the solution by #William Hu:
func popup(caller:UIViewController, style:UIAlertControllerStyle? = UIAlertControllerStyle.alert,
title:String, message:String, buttonTexts:[String], buttonStyles:([UIAlertActionStyle?])? = nil,
handlers:[((UIAlertAction) -> Void)?], animated:Bool? = nil, completion: (() -> Void)? = nil) {
let alert = UIAlertController(title: title, message: message, preferredStyle: style!)
for i in 0..<buttonTexts.count {
alert.addAction(UIAlertAction(title: buttonTexts[i],
style: (buttonStyles == nil || i >= buttonStyles!.count || buttonStyles![i] == nil ?
UIAlertActionStyle.default : buttonStyles![i]!),
handler: (i >= handlers.count || handlers[i] == nil ? nil : handlers[i]!)))
}
caller.present(alert, animated: animated != nil ? animated! : true, completion: completion)
}
Single function gives Alert by default and can optionally be used for ActionSheet.
Arrays buttonTexts, buttonStyles and handlers can be of unequal sizes as per requirement.
Actions can be styled.
Animated can be specified.
Optional block can be specified to be executed when presentation finishes.
Usage:
popup(caller: self, style: UIAlertControllerStyle.alert,
title: "Title", message: "Message",
buttonTexts: ["Destructive", "Cancel", "OK"],
buttonStyles: [UIAlertActionStyle.destructive, UIAlertActionStyle.cancel],
handlers: [nil], animated: false)
You can use my Utility class created for Show Alert in Swift4. Its super easy to use just by writing single line of code:
Show Simple Alert
#IBAction func showDefaultAlert(_ sender: Any) {
Alert.showAlert(title:"Alert", message:"Default Alert")
}
Demo code link: https://github.com/smindia1988/EasyAlertInSwift4
If you want to present from AppDelegate windows you can use like this
UIApplication.sharedApplication().delegate?.window.rootViewController?.presentViewController(vc, animated: true, completion: nil)
This is also the way you can present on top of the View Controller available.
UIApplication.topViewController()?.present(alertViewController!, animated: true, completion: nil)

ELCImagePickerController delegate methods not called

I'm trying to build an app using ELCImagePickerController. I found that I could select multiple pictures. However, the ELCImagePickerController delegate method was not called.
This is my code:
#IBAction func uploadImages(sender: AnyObject) {
// Create the alert controller
//var alertController = UIAlertController(title: "", message: "", preferredStyle: .Alert)
var alertController = UIAlertController(title: nil, message: nil, preferredStyle: .Alert)
// Create the actions
var takeAction = UIAlertAction(title: "Take Photos", style: UIAlertActionStyle.Default) {
UIAlertAction in
NSLog("Take Photos Pressed")
}
var selectAction = UIAlertAction(title: "Select Photos", style: UIAlertActionStyle.Default) {
UIAlertAction in
NSLog("Select photos Pressed")
var imagePicker = ELCImagePickerController(imagePicker: ())
imagePicker.maximumImagesCount = 2
imagePicker.returnsOriginalImage = false
imagePicker.returnsImage = true
imagePicker.onOrder = true
imagePicker.delegate = self
self.presentViewController(imagePicker, animated: true, completion: nil)
}
var cancelAction = UIAlertAction(title: "Cancel", style: UIAlertActionStyle.Cancel) {
UIAlertAction in
NSLog("Cancel Pressed")
}
// Add the actions
alertController.addAction(takeAction)
alertController.addAction(selectAction)
alertController.addAction(cancelAction)
// Present the controller
self.presentViewController(alertController, animated: true, completion: nil)
}
}
func elcImagePickerController(picker: ELCImagePickerController!, didFinishPickingMediaWithInfo info:[AnyObject]!) {
NSLog("controller executed.")
}
You need to set the ImagePicker delegate
imagePicker.imagePickerDelegate = self
Are your NSLog statements ever getting called? One thing I notice in your trailing closure syntax is that you're using the type name versus a variable of that type. For instance, you're writing UIAlertAction in ... vs alertAction in .... You should be providing a name to be used within the closure rather than the type itself. If the rest of the closure is not executing, then the delegate is never being set, and therefore delegate methods are never called.

Resources