ViewController in other file - ios

I'm a novice with Swift 2. I tried to create a UIAlertController in another file.
This is my code :
class my_view_error: UIViewController {
func my_error(my_title: String, my_message: String) {
let alertController = UIAlertController(title: my_title, message: my_message, preferredStyle: .Alert)
let OKAction = UIAlertAction(title: "OK", style: .Default) { (action) in }
alertController.addAction(OKAction)
self.presentViewController(alertController, animated: true) {}
}
}
I'm receiving a compile time error:
Warning: Attempt to present on whose view is not in the window hierarchy!
My question is, is it possible to create a ViewController in a file other than ViewController.swift?

You created a UIViewController subclass called my_view_error. The UIViewController method presentViewController expects self (a my_view_error) to be on the screen at the time the method is called.
Your my_error function is perfectly fine, but it should be moved to a view controller that is actually on the screen. There is no need to create a UIViewController just to present a UIAlertController. In fact, UIAlertController is a UIViewController, and does not need help from a custom view controller.
Just like with any view controller, the view controller that is presenting the alert must be on the screen in order to perform the presentation.
Each view controller may have its own .swift file. There is no need to create one for a UIAlertController, however.
Heres a version of the function that uses rootViewController on UIApplication to get the currently on-screen view controller, and then uses that to present your alert:
func my_error(my_title: String, my_message: String) {
let alertController = UIAlertController(title: my_title, message: my_message, preferredStyle: .Alert)
let OKAction = UIAlertAction(title: "OK", style: .Default) { (action) in }
alertController.addAction(OKAction)
// Uses UIApplication.sharedApplication().keyWindow!.rootViewController! instead of self
UIApplication.sharedApplication().keyWindow!.rootViewController!.presentViewController(alertController, animated: true) {}
}

Related

How can I declare a function or variable in a file and use it always in Swift [duplicate]

This question already has answers here:
create alert function in all view controllers - swift
(7 answers)
Closed 5 years ago.
Currently I am working on a project and I have to use an alert message almost in every view controller. Particularly this one:
func showMessage(myMessage: String) {
let myAlert = UIAlertController(title: "ALERT", message: myMessage, preferredStyle: UIAlertControllerStyle.alert)
let okAction = UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: nil)
myAlert.addAction(okAction)
self.present(myAlert, animated: true, completion: nil)
}
Using the same code over an over again makes my code verbose. This scenario goes with some other functions and some variables also. How can I declare this function somewhere in a different file and use it whenever necessary? Shall I use a singleton pattern like:
static let sharedInstance = viewController()
If so, please show me an example.
The best way to do this is using an extension such as:
extension UIViewController {
func showMessage(myMessage: String) {
let myAlert = UIAlertController(title: "ALERT", message: myMessage, preferredStyle: UIAlertControllerStyle.alert)
let okAction = UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: nil)
myAlert.addAction(okAction)
self.present(myAlert, animated: true, completion: nil)
}
}
This way you can use it in any class subclassed from UIViewController. You could also just drop the function into a swift file as a "global function" but that's not very clean.
Your function showMessage(myMessage:) is common to all your ViewControllers. So you can either :
Use a super class ViewController and implement your function in there :
class BaseViewController {
func showMessage(myMessage: String) { ... }
}
Use swift protocols :
protocol MessageHelper {}
extension MessageHelper where Self: UIViewController {
func showMessage(myMessage: String) { ... }
}
extension MyViewController: MessageHelper {}
Using a shared instance that handles all your message could be another way to go.
class MessageController {
static let shared = MessageController()
private init() {}
func showMessage(myMessage: String, viewController: UIViewController) { ... }
}
And just call MessageController.shared.showMessage(myMessage:viewController:) when you need to display a message.
Using a message controller will give you more possibilities, you can either count the number of messages displayed or filter messages to display in the same and one place.
Use a custom class for the view controllers in your app. Instead of subclassing UIViewController, each of your view controllers would subclass your CustomViewController class. Here's an example:
class CustomViewController: UIViewController {
func showMessage(myMessage: String) {
let myAlert = UIAlertController(title: "ALERT", message: myMessage, preferredStyle: UIAlertControllerStyle.alert)
let okAction = UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: nil)
myAlert.addAction(okAction)
self.present(myAlert, animated: true, completion: nil)
}
}
Each view controller you create that subclasses CustomViewController would then have access to the showMessage function.

How do I display an alert popup from another Class that is not a UIViewController?

I have the following Swift 3 code in a separate swift file in another class.
class Login{
func showAlert(message :String){
let alertController2 = UIAlertController(title: "Error", message: "A error occured when checking credentials, try again later.", preferredStyle: .alert)
let defaultAction = UIAlertAction(title: "OK", style: .default, handler: nil)
alertController2.addAction(defaultAction)
self.present(alertController2, animated: true, completion: nil)
}
}
But I get a red error:
Use of unresolved identifier 'UIAlertController'
How can I create a popup informing the user that something went wrong?
First you need to import UIKit in order to make UIAlertController visible to your class.
This code will obtain the current view controller even if it's a modal.
func topViewController() -> UIViewController? {
guard var topViewController = UIApplication.shared.keyWindow?.rootViewController else { return nil }
while topViewController.presentedViewController != nil {
topViewController = topViewController.presentedViewController!
}
return topViewController
}
So you can now obtain the controller and present the alert on it:
topViewController()?.present(alertController2, animated: true, completion: nil)
Firstly, you need:
import UIKit
But your larger problem is that the present() method you're trying to use is a method of UIViewController objects, and also it only works on View Controllers whose view is already a part of the view hierarchy.
So I think you need to refactor your code a bit, and decide which view controller should launch your alert.
import Foundation
import UIKit
class Utility: NSObject{
func showAlert(_ VC : UIViewController, andMessage message: String , handler :#escaping(UIAlertAction) -> Void){
let alert = UIAlertController(title: "Error", message: message , preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "Ok", style:UIAlertActionStyle.default, handler: handler))
VC.present(alert, animated: true, completion: nil)
}
}
Try this, this will work.

iOS. Attempt to present UIAlertController on UIViewController whose view is not in the window hierarchy

Swift 3, Xcode 8.1. I want to display UIAlertController in UIViewController.
I have methods:
private static func rootViewController() -> UIViewController {
// cheating, I know
return UIApplication.shared.keyWindow!.rootViewController!
}
static func presentAlert(_ message: String) {
let alertView = UIAlertController(title: "RxExample", message: message, preferredStyle: .alert)
alertView.addAction(UIAlertAction(title: "OK", style: .cancel) { _ in })
rootViewController().present(alertView, animated: true, completion: nil)
}
Full code of this class you can find here
I call the method presentAlert in viewDidLoad:
override func viewDidLoad() {
super.viewDidLoad()
DefaultWireframe.presentAlert("test")
...
}
and got the warning:
Warning: Attempt to present UIAlertController: 0x7f904ac0d930 on UIViewController: 0x7f904ad08040 whose view is not in the window hierarchy!
How to avoid the warning and display the Alert?
It works when I try to show Alert in initial ViewController, but it doesn't work in another ViewController connected using push segue with initial VC.
In viewDidLoad, your app has not presented the view controller to the user yet, so an alert cannot be shown. Try executing that code in viewDidAppear
I had similar problem/case where action sheet was not showing on a ViewController which was pushed on another ViewController. The error it was giving was also similar as your's. What you have done works perfectly on normal ViewControllers but just doesn't work on the ViewControllers which are pushed over other ViewController.
I solved the problem by making the object of class UIAlertController as an instance variable of my class rather than keeping it local inside the triggering function.
So in your case, try declaring var alertView: UIAlertController? at top of the class where instance variables are declared and then just initialise it in your desired triggering function to use it like this:
static func presentAlert(_ message: String) {
self.alertView = UIAlertController(title: "RxExample", message: message, preferredStyle: .alert)
alertView.addAction(UIAlertAction(title: "OK", style: .cancel) { _ in })
rootViewController().present(alertView, animated: true, completion: nil)
}
Might be some bug from Apple's side in maintaining reference which was causing this problem. But work around I wrote above works perfect.

UIAlert error, whose view is not in the window hierarchy

I am working on my network connectivity,
I put checking reachability code in viewDidLoad, so that I notify User by alerting if there is not network.
Here is my code for this,
class ViewController: UIViewController {
var reachability : Reachability?
override func viewDidLoad() {
super.viewDidLoad()
if reachability?.isReachable() == true
{
print("reachable")
}else{
let myAlert = UIAlertController(title: "No network", message:
"Your network is not working", preferredStyle:
UIAlertControllerStyle.Alert)
let okAction = UIAlertAction(title: "Ok", style:
UIAlertActionStyle.Default, handler: nil)
myAlert.addAction(okAction)
self.presentViewController(myAlert, animated: true, completion:
nil)
}
but If I try on my simulator or cellphone,
it shows error message that
2015-11-08 16:43:52.173 PracticeReachability[5494:2661290] Warning: Attempt to present on whose view is not in the window hierarchy!
I tried put
var myAlert = UIAlertController()
and
var myAlert :UIAlertController!
both doesn't make it work.
Question
My other Alerts work fine in same ViewController.
Why only this is not working?
And How can I make it work?
I don't think the view controller's view has been added to the screen yet in viewDidLoad. Try moving your code to viewDidAppear.

swift - UIAlertController takes the user back to the first ViewController

I use UIAlertController in my app and whenever the user clicks OK, it takes the user back to the first viewcontroller.
var activityIndicator: UIActivityIndicatorView = UIActivityIndicatorView()
func displayAlert(title:String, error: String){
var alert = UIAlertController(title: title, message: error, preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: "OK", style: .Default, handler: { action in
self.dismissViewControllerAnimated(true, completion: nil)
}))
self.presentViewController(alert, animated: true, completion: nil)
}
I use the function above to show the alert at any point on the viewcontroller.
What does that happen? How can I keep the user on the current viewcontroller?
In your action you are calling self.dismissViewController - in this context "self" is the current view controller, not your alert view, so you are actually requesting the removal of the current view controller.

Resources