Present a view controller from a closure - swift

I have a closure as a handler for items that are added to a UIAlertController. I receive the id value inside the closure as expected. (I don't use it in the snippet).
But the problem that I have is that I want to switch to another view controller. But I do this call inside the closure.
I get the following error:
Value of type '(ChooseProfile) -> () -> (ChooseProfile)' has no member 'present'
How can I switch to another view controller from within my closure?
class ChooseProfile: UIViewController {
let closure = { (id: String) in { (action: UIAlertAction!) -> Void in
let storyBoard : UIStoryboard = UIStoryboard(name: "Main", bundle:nil)
let nextViewController = storyBoard.instantiateViewController(withIdentifier: "test") as! UIViewController
self.present(nextViewController, animated:true, completion:nil)
}}
}
I use this closure because of this:
override func viewDidAppear(_ animated: Bool) {
let alert = UIAlertController(title: "Choose a Device", message: "Which device would you like to use in this app?", preferredStyle: UIAlertControllerStyle.alert)
for dev in (DataSingleton.instance.getDevices())! {
alert.addAction(UIAlertAction(title: dev.getName(), style: UIAlertActionStyle.default, handler: closure(dev.getId())))
}
alert.addAction(UIAlertAction(title: "Add a new Profile", style: UIAlertActionStyle.destructive, handler: nil))
// show the alert
self.present(alert, animated: true, completion: nil)
}
I add the alert actions based on a list of devices. And I want to retrieve the id when I click on a device.

The problem is that you are using self in your closure, but that self doesn't refer to the instance of your VC because it hasn't been fully created yet. Instead, why not pass the viewController to the closure when you call it?
class ChooseProfile: UIViewController {
let closure = { (id: String, vc: UIViewController) in { [weak vc] (action: UIAlertAction!) -> Void in
let storyBoard : UIStoryboard = UIStoryboard(name: "Main", bundle:nil)
let nextViewController = storyBoard.instantiateViewController(withIdentifier: "test")
vc?.present(nextViewController, animated:true, completion:nil)
}}
}
override func viewDidAppear(_ animated: Bool) {
let alert = UIAlertController(title: "Choose a Device", message: "Which device would you like to use in this app?", preferredStyle: UIAlertControllerStyle.alert)
for dev in (DataSingleton.instance.getDevices())! {
alert.addAction(UIAlertAction(title: dev.getName(), style: UIAlertActionStyle.default, handler: closure(dev.getId(), self)))
}
alert.addAction(UIAlertAction(title: "Add a new Profile", style: UIAlertActionStyle.destructive, handler: nil))
// show the alert
self.present(alert, animated: true, completion: nil)
}

Related

Segue to another storyboard without using Storyboard Reference?

I have a button in my App which gives an alert when clicked, and only if the user clicks "OK" - I want to send them to another storyboard. Is it possible to do this in the ViewController? Without a storyboard reference? How will the code look like?
Based on the updated details from your comments, you will need to instantiate your viewController in your storyboard and perform the navigation manually like so:
let alert = UIAlertController(title: "Alert!", message: "Do the thing", preferredStyle: .alert)
let action = UIAlertAction(title: "OK", style: .default) { (action) in
let storyboard = UIStoryboard(name: "Bussturen", bundle: nil)
let viewController = storyboard.instantiateViewController(identifier: "BussturenViewController") as! BussturenViewController
//Do any more setup you might need to do for your viewController before navigation
self.navigationController?.pushViewController(viewController, animated: true)
}
alert.addAction(action)
self.present(alert, animated: true, completion: nil)
Please also note that this assumes that in the Identity Inspector in your storyboard you have your BussturenViewController set as "BussturenViewController"
Place the below code
// Your button tap function
#IBAction func buttonClickAction(_ sender: Any) {
let alert = UIAlertController(title: "", message: "Show me next view controller", preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: { _ in
self.displayNextViewController()
}))
alert.addAction(UIAlertAction(title: "NO", style: .default, handler: nil))
self.present(alert, animated: true, completion: nil)
}
// Function which loads next view controller
func displayNextViewController() {
let nextViewController = NextViewController() as UIViewController
self.navigationController?.pushViewController(nextViewController, animated: true)
}
You can customize the nextVC instantiation based on your requirement. Hope this helps for you!
by looking at the help from all the answers, I was able to find a solution:
func displayNextViewController() {
let storyBoard : UIStoryboard = UIStoryboard(name: "Bussturen", bundle:nil)
let nextViewController = storyBoard.instantiateViewController(withIdentifier: "BussturenViewController") as! BussturenViewController
nextViewController.modalPresentationStyle = .fullScreen
self.present(nextViewController, animated:false, completion:nil)
}
#IBAction func bussturenTapped(_ sender: UIButton) {
let alert = UIAlertController(title: "", message: "Show me next view controller", preferredStyle: UIAlertController.Style.alert)
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: {_ in self.displayNextViewController() }))
alert.addAction(UIAlertAction(title: "NO", style: .default, handler: nil))
self.present(alert, animated: true, completion: nil)
}

segue/push to different view controller from IBAction function?

I am trying to push to the next viewcontroller programatically, such that I can save data in core data and then push to the next view controller. Basically when I tap a button with an IBAction, it will do all this. However, I am getting a thread 1 SIGABRT error whenever I use this code in my IBAction func:
#IBAction func onPlusTapped(){
let alert = UIAlertController(title: "New Course", message: nil, preferredStyle: .alert)
alert.addTextField { (textField) in
textField.placeholder = "Course"
}
let action = UIAlertAction(title: "New", style: .default){ (_) in
let course = alert.textFields!.first?.text!
print(course)
let coursy = Course(context: PersistenceService.context)
coursy.name = course
PersistenceService.saveContext()
}
alert.addAction(action)
present(alert, animated: true, completion: nil)
// jump to next view controller
segway()
}
func segway(){
let storyBoard: UIStoryboard = UIStoryboard(name: "viewcontroller", bundle: nil)
let balanceViewController = storyBoard.instantiateViewController(withIdentifier: "viewcontroller") as! ViewController
self.present(balanceViewController, animated: true, completion: nil)
}
}
If you want to jump to your next viewController once Course saved, you have to call the func segway() in the UIAlertAction completion block.
Example:
let action = UIAlertAction(title: "New", style: .default){ (_) in
let course = alert.textFields!.first?.text!
print(course)
let coursy = Course(context: PersistenceService.context)
coursy.name = course
PersistenceService.saveContext()
// jump to next view controller
segway()
}
Also double check your storyboard name and balanceViewController's identifier is "viewcontroller".
Why are you jumping segway() and present alert at the same time? shouldn't you put segway() inside your alert action?
Please see the following code.
#IBAction func onPlusTapped() {
let alert = UIAlertController(title: "New Course", message: nil, preferredStyle: .alert)
alert.addTextField { (textField) in
textField.placeholder = "Course"
}
let action = UIAlertAction(title: "New", style: .default){ (_) in
let course = alert.textFields!.first?.text!
print(course)
let coursy = Course(context: PersistenceService.context)
coursy.name = course
PersistenceService.saveContext()
// jump to next view controller
segway()
}
alert.addAction(action)
present(alert, animated: true, completion: nil)
}
func segway() {
let storyBoard: UIStoryboard = UIStoryboard(name: "viewcontroller", bundle: nil)
let balanceViewController = storyBoard.instantiateViewController(withIdentifier: "viewcontroller") as! ViewController
self.present(balanceViewController, animated: true, completion: nil)
}
}
Hope this help!

swift 3 make alert with action button and present controller

everyone I am making Alert from my AlertController, but I want after action button "OK" is pressed, open LoginController, how I can do that?I tried to do that just like below, but it's not working
AlertController:
class AlertController: NSObject {
class func showErrorWith(title:String? = nil, message:String? = nil, controller: UIViewController , complition:(() -> ())?){
let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: { (action) in
// let storyboard = UIStoryboard(name: "Main", bundle: nil)
// let viewController = storyboard.instantiateViewController(withIdentifier :"LoginVC")
// self.present(viewController, animated: true)
//
}))
controller.present(alert, animated: true, completion: nil)
}
It's happen because you call self.present in this code self - It's kind of NSObject.
Just call controller.present(viewController, animated:true);
func ShowAlert(vc: UIViewController, title: String, message:String, affirmButton: String = "OK", cancelButton: String = "Cancel", onAffirmation: (Void) -> (Void) = { })
{
let alertView = UIAlertController(title: title, message: message, preferredStyle: .Alert)
alertView.addAction(UIAlertAction(title: affirmButton, style: UIAlertActionStyle.Default, handler:{(action) in
onAffirmation()
}))
alertView.addAction(UIAlertAction(title: cancelButton, style: UIAlertActionStyle.Default, handler: nil))
vc.presentViewController(alertView, animated: true, completion: nil)
}

UIAlertController on button click

Opening the UIAlertController on button click, the action is going to open but main issue is the UIAlertAction methods are not performed on its click. Here is Code block :
class HomeViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// getData()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
//menuBtn is the button
#IBAction func menuBtn(sender: UIButton) {
let optionMenu = UIAlertController(title: nil, message: nil, preferredStyle: .ActionSheet)
let orders = UIAlertAction(title: "Orders", style: .Default, handler: { (alert: UIAlertAction!) -> Void in
let alertViewController = self.storyboard?.instantiateViewControllerWithIdentifier("OrdersViewController") as! OrdersViewController
self.presentViewController(alertViewController, animated: true, completion: nil)
})
let about = UIAlertAction(title: "About", style: .Default, handler: {(alert: UIAlertAction!) -> Void in
let aboutObject = self.storyboard?.instantiateViewControllerWithIdentifier("AboutViewController") as! AboutViewController
self.presentViewController(aboutObject, animated: true, completion: nil)
})
let contactUs = UIAlertAction(title: "Contact Us", style: .Default, handler: {(alert: UIAlertAction!) -> Void in
let alertViewController = self.storyboard?.instantiateViewControllerWithIdentifier("ContactViewController") as! ContactViewController
self.presentViewController(alertViewController, animated: true, completion: nil)
})
let login = UIAlertAction(title: "LogIn", style: .Default, handler: {(alert: UIAlertAction!) -> Void in
let alertViewController = self.storyboard?.instantiateViewControllerWithIdentifier("LoginViewController") as! LoginViewController
self.presentViewController(alertViewController, animated: true, completion: nil)
})
let cancelAction = UIAlertAction(title: "Cancel", style: .Cancel, handler: nil)
optionMenu.addAction(orders)
optionMenu.addAction(about)
optionMenu.addAction(contactUs)
optionMenu.addAction(login)
optionMenu.addAction(cancelAction)
self.presentViewController(optionMenu, animated: true, completion: nil)
}
This is code is working fine, I have checked it's opening new viewController as well.
Cross check points:
Controller class and stroybaord are connected
Storyboard ID has been assigned
IBAction must be connected to IBOutlet
On the Button click Action you have to write code.. Try This code.
let alert = UIAlertController(title: "Saved", message: "Selected Frame is Saved", preferredStyle: .Alert)
alert.addAction(UIAlertAction(title: "Ok", style:.Default , handler: { (UIAlertAction) in
}))
//Add action like this
self.presentViewController(alert, animated: true, completion: nil)
Still need any help feel free to ask.
First of all check if the button action is touch up inside. Make the button action as touch up inside. Below code works for me. Hope this works for you as well. Change the action title according to your need.
#IBAction func menuBtn(sender: AnyObject) {
let actionSheet = UIAlertController()
let criticalAction = UIAlertAction(title : "CRITICAL" , style : UIAlertActionStyle.Default){
(action) in
//This section will be executed when the buttons are pressed
//Do your work here.
debugPrint("CRITICAL")
}
let highAction = UIAlertAction(title : "HIGH" , style : UIAlertActionStyle.Default){
(action) in
//This section will be executed when the buttons are pressed
//Do your work here.
debugPrint("HIGH")
}
actionSheet.addAction(criticalAction)
actionSheet.addAction(highAction)
self.presentViewController(actionSheet, animated: true, completion: nil)
}

How to navigate ViewController when clicking button in alertView

Given the code below, when I click the "Accept" button, it doesn't navigate to SecondViewController, it only shows a black screen. Any help appreciated.
let alertController = UIAlertController(title: tit!, message: "Book Now!", preferredStyle: .Alert)
let declineAction = UIAlertAction(title: "Decline", style: .Cancel, handler: nil)
alertController.addAction(declineAction)
let acceptAction = UIAlertAction(title: "Accept", style: .Default) { (_) -> Void in
let nv=SecondViewController()
self.presentViewController(nv, animated:true, completion:nil)
}
presentViewController(alertController, animated: true, completion: nil)
alertController.addAction(acceptAction)
To open any UIViewController just must instantiate it. And what you are doing is just creating the object of the class.
To do so:
let nv = self.storyboard!.instantiateViewControllerWithIdentifier("storyboardidentifier") as! SecondViewController
self.presentViewController(nv, animated:true, completion:nil)
This is open your UIViewController as wants!
Create a segue link from your current view controller to the SecondViewController and give it an identifier in the storyboard and you can use the following code
let alert = UIAlertController(title: "Options", message: "Book Now!", preferredStyle: .Alert)
alert.addAction(UIAlertAction(title: "Cancel", style: UIAlertActionStyle.Cancel, handler: nil))
alert.addAction(UIAlertAction(title: "Accept", style: UIAlertActionStyle.Default, handler: { (alertAction) -> Void in
self.performSegueWithIdentifier("FirstToSecond", sender: self)
}))
self.presentViewController(alert, animated: true, completion: nil)
Note: your first view controller will be the anchor.
If you have embedded your SecondViewController in a navigation controller and also want to pass the value, you can add the following
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "FirstToSecond" {
let nav = segue.destinationViewController as! UINavigationController
let svc = nav.viewControllers[0] as! SecondViewController
//svc.delegate = self //uncomment this if you need to set a delegate as well
svc.value = ""
}
}
This is using xCode 7 & Swift 2. Hope this helps :)