Basically I am currently adding functionX to everywhere i present an UIAlertController as seen below:
let alert = UIAlertController(title: "", message: "", preferredStyle: .alert)
let okAction = UIAlertAction(title: "ok", style: .default)
alert.addAction(okAction)
functionX(actionSheet: alert, controller: self)
self.present(alert, animated: true, completion: nil)
// or it can be
// tableviewController.present(alert, animated: true, completion: nil)
Instead of calling functionX every time, I want to override a present method and call functionX there. I attempted the following:
extension UIViewController {
override func present(_ viewControllerToPresent: UIViewController, animated flag: Bool, completion: (() -> Void)? = nil) {
if (viewControllerToPresent is UIAlertController) {
functionX(actionSheet: viewControllerToPresent, controller: /* what should this be? */ )
}
super.present() //error here
}
}
Is this an appropriate approach? Can you help me fill the missing parameters?
i.e.:
What should be the controller? What would self or tableviewController from the first code stub be in the overriding present function?
How should I call the present method in the overriding present function?
According to the Swift guide,
Extensions can add new functionality to a type, but they cannot override existing functionality.
So you shouldn't really be overriding an existing method in a UIViewController in an extension.
What you could do is, to add your own present, called functionXAndPresent:
extension UIViewController {
func functionXAndPresent(_ viewControllerToPresent: UIViewController, animated flag: Bool, completion: (() -> Void)? = nil) {
if (viewControllerToPresent is UIAlertController) {
// to answer your second question, you should use "self" here
functionX(actionSheet: viewControllerToPresent, controller: self)
}
present(viewControllerToPresent, animated: flag, completion: completion)
}
}
You can't do this by overriding because as you have found out, you can't really refer to the "non-overridden" method at the end. super.present doesn't work because you are in an extension, not a subclass.
You can simply create a common method showAlert(with:and:) in a UIViewController extension and call functionX when the alert is presented, i.e.
extension UIViewController {
func showAlert(with title: String?, message: String?) {
let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
let okAction = UIAlertAction(title: "OK", style: .default)
alert.addAction(okAction)
self.present(alert, animated: true, completion: {
self.functionX(actionSheet: alert, controller: self)
})
}
func functionX(actionSheet: UIAlertController, controller: UIViewController) {
//your code here...
}
}
Usage:
Call the showAlert(with:and:) method from whatever controller you want to, be it a UIViewController or a UITableViewController or any other, i.e
self.showAlert(with: "Alery..!!!", message: "This is a sample alert.")
Overrides of NSObject's derivatives in Swift's static extensions is only for Objective-C compatibility. You cannot override in extensions of pure Swift declarations. Think of it in such manner that if the class itself adds an override, and then a static extension adds an override. Which implementation should the linker link? And which implementation does super call refer to? In Objective-C this behavior is undefined, in Swift the extension override is ignored all together.
What you could do instead is move the overriding function from the extension directly to the class.
Related
New swift user here. I would like to open a pop up (controlled by a separate view controller) over the normal view controller as part of a function call (i.e. there is no button that is pressed or similar). How do I write this? I would also like to send information to that influences the images that are shown in the pop up.
I have previously managed to open a modal pop-up via a button press. I don't really know if there is something peculiar about these kinds of popups but if there are I'd like to have the pop up above look the same.
I hope this is clear enough for someone to understand what I need.
You can add a method in super view controller like this:
class MainViewController: UIViewController {
// MARK: - View methods
override func viewDidLoad() {
super.viewDidLoad()
}
/// This Method is used for Alert With Action Method
func showAlertViewWithPopAction(message: String, title: String) {
let alert = UIAlertController(title: title, message: message, preferredStyle: UIAlertControllerStyle.alert)
let okAction = UIAlertAction(title: "OK", style: .default) { [weak self] _ in
///add you logic here
}
alert.addAction(okAction)
self.present(alert, animated: true, completion: nil)
}
}
And Inherit this controller in every controller like this and call this method from the controller like this:
class FirstViewController: MainViewController {
// MARK: - View methods
override func viewDidLoad() {
super.viewDidLoad()
///Call alert method according to your use
Self.showAlertViewWithPopAction(message: "This is demo alert", title: "Alert")
}
}
I'm working on a legacy Swift 2.2 project, and I want to implement some well-known protocol-oriented practices to my code.
protocol SuccessPresenting {
func presentSucess(title: String, message: String)
}
extension SuccessPresenting where Self: UIViewController {
func presentSucess(title: String?, message: String) {
let alertController = UIAlertController(title: title, message: message, preferredStyle: .Alert)
let dismissAction = UIAlertAction(title: "ОК", style: .Default, handler: nil)
alertController.addAction(dismissAction)
self.presentViewController(alertController, animated: true, completion: nil)
}
}
class NewViewController: UIViewController, SuccessPresenting {
func foo() {
presentSucess(nil, message: "Done!")
}
}
Though, it is works on Swift 3.1, here I get an error: The NewViewController doesn't conform to protocol SuccessPresenting
But why should I write protocol implementation in my VC, as I have already done that using protocol extension?
I'll appreciate any help.
Please remind, this is Swift 2.2
Is this a direct paste? Because your extension contains an optional instead of a regular string, whereas your protocol has a normal String. This might cause the compiler to regard it is a different method, rendering the optionallness of your protocol invalid in this particular case.
I have a UIViewController which loads up some json data from the server. If the server is down or the user has data turned off I throw up an alert telling the user such. This is done using a UIAlertController. This works great. So I put this into an extension since it is used by all UIViewControllers which need data. Now the UIAlertController has an action set as well
Alert code
extension UIViewController {
func connectionLost(){
var message = "Your device has lost connection to the server. Check that you have a valid internet connection and then retry."
let alertController = UIAlertController( title: "Connection Lost",
message: message,
preferredStyle: .alert)
let retryAction = UIAlertAction(title:"Retry", style: .default, handler: {
action in
//call function in the viewcontroller that raised this alert to reload the data
})
alertController.addAction(retryAction)
self.present(alertController, animated: true, completion: nil)
}
}
When the user taps the retry button I want to call a function in the uiviewcontroller that raised the alert.
I tried creating a delegate in the extension but struggled with getting it wired up like you do in a class. What sort of approaches are there to call a function from an extension in the viewcontroller that raised the alert?
You should create a BaseViewController and use Inheritance. It could be useful for other implementations too.
class BaseViewController: UIViewController {
func onRetryClick() {
// override to customize or write here the common behaviour
}
}
class FirstViewController: BaseViewController {
override func onRetryClick() {
// do something specific for FirstViewController
}
}
class SecondViewController: BaseViewController {
override func onRetryClick() {
// do something specific for SecondViewController
}
}
class ThirdViewController: BaseViewController {
// if you don't override this method, super class (BaseViewController) implementation will be executed
}
extension BaseViewController {
func connectionLost(){
var message = "Your device has lost connection to the server. Check that you have a valid internet connection and then retry."
let alertController = UIAlertController( title: "Connection Lost",
message: message,
preferredStyle: .alert)
let retryAction = UIAlertAction(title:"Retry", style: .default, handler: { action in
self.onRetryClick()
})
alertController.addAction(retryAction)
self.present(alertController, animated: true, completion: nil)
}
}
Hope this makes sense.
class MyVC: UIViewController {
func retry() {
}
func checkConnection() {
connectionLost { (retry) -> (Void) in
if retry {
self.retry()
}
}
}
}
extension UIViewController {
func connectionLost(completion: #escaping (_ retry: Bool) -> (Void)) {
let message = "Your device has lost connection to the server. Check that you have a valid internet connection and then retry."
let alertController = UIAlertController( title: "Connection Lost",
message: message,
preferredStyle: .alert)
let retryAction = UIAlertAction(title:"Retry", style: .default, handler: {
action in
completion(true)//may be 'false', you decide
})
alertController.addAction(retryAction)
self.present(alertController, animated: true, completion: nil)
}
}
I am having trouble calling the tweet function. I keep getting errors with the parameters. I also tried just (AnyObject) and got
Error: argument type does not conform to expected...
I am new to swift and not sure how to get this running. Tried everything I can think of. Thank you
// from GameScene
var vc = ViewController()
vc.tweetAction(sender: AnyObject)
//error: cannot create single-element tuple with an element label
//function in View Controller below
#IBAction func tweetAction(sender: AnyObject){
if SLComposeViewController.isAvailableForServiceType(SLServiceTypeTwitter){
let tweetController = SLComposeViewController(forServiceType: SLServiceTypeTwitter)
tweetController.setInitialText("I Scored on this app")
self.presentViewController(tweetController, animated: true, completion: nil)
}
else{
let alert = UIAlertController(title: "Accounts", message: "Please log into your twitter to share", preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: "Dismiss", style: UIAlertActionStyle.Default, handler: nil))
alert.addAction(UIAlertAction(title: "Settings", style: UIAlertActionStyle.Default, handler: { (UIAlertACtion) in
let settingsURL = NSURL(string:UIApplicationOpenSettingsURLString)
if let url = settingsURL{
UIApplication.sharedApplication().openURL(url)
}
}))
self.presentViewController(alert, animated: true, completion: nil)
}
}
You are passing a type to your method invocation instead of an instance. You also are including a label for the first parameter, which is incorrect. Try:
vc.tweetAction(self)
Try to change sender parameter type from AnyObject to UILabel
var vc = ViewController()
vc.tweetAction(yourUILabelInstance)
And don't forget to modify tweetAction function as well
#IBAction func tweetAction(sender: UILabel){
...
}
I am trying to create an alert view that performs a certain action when the button is clicked. I have tried creating a new class for the alert view, but when I try to add an action to the alert view controller, Xcode tells me that it is expecting a declaration, although the variable is declared just two steps above the line where the error occurs. Here's the code
class alerts: UIAlertController {
var alertThenGenerateNewThingController: UIAlertController = UIAlertController()
var generateNewThingOkButton = UIAlertAction (title: "OK", style: UIAlertActionStyle.Default) {
UIAlertAction in
println ("generate new thing action")
}
alertThenGenerateNewThingController.addAction (generateNewThingOkButton) // Here is where Xcode says it expected a declaration
func alertThenGenerateNewThing (alertTitle: String, alertMessage: String) {
alertThenGenerateNewThingController = UIAlertController (title: alertTitle, message: alertMessage, preferredStyle: .Alert)
self.presentViewController (alertThenGenerateNewThingController, animated: true, completion: nil)
}
}
You can't subclass UIAlertController, for one thing. Second of all, you can only interact with object properties inside of methods, functions, or the global scope. Run this code inside of the view controller where you plan on presenting your alert:
class viewController: UIViewController {
var alertThenGenerateNewThingController: UIAlertController = UIAlertController()
var generateNewThingOkButton = UIAlertAction (title: "OK", style: UIAlertActionStyle.Default) {
UIAlertAction in
println ("generate new thing action")
}
override func viewDidLoad() {
alertThenGenerateNewThingController.addAction(generateNewThingOkButton)
}
func alertThenGenerateNewThing (alertTitle: String, alertMessage: String) {
alertThenGenerateNewThingController = UIAlertController (title: alertTitle, message: alertMessage, preferredStyle: .Alert)
self.presentViewController (alertThenGenerateNewThingController, animated: true, completion: nil)
}
}