segue/push to different view controller from IBAction function? - swift

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!

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)
}

Present a view controller from a closure

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)
}

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 :)

Creating a pop up dialog alert

I'm trying to create a popup uialert sort of to create a confirmation box. I have a button in ViewControllerTwo and when pressed navigates back to ViewControllerOne, however I want to create a popup message that asks to confirm (Yes or No) if I really want to navigate to ViewControllerOne. If yes it goes backs to ViewOne, if no it stays on the ViewTwo. How do I do this?
#IBAction func showAlertTapped(sender: AnyObject) {
//Create the AlertController
let myAlertController: UIAlertController = UIAlertController(title: "Hey..!", message: "Are You sure to Do some stuff??", preferredStyle: .Alert)
//Create and add the Cancel action
let cancelAction: UIAlertAction = UIAlertAction(title: "Cancel", style: .Cancel) { action -> Void in
//Do some stuff
}
myAlertController.addAction(cancelAction)
//Create and an option action
let nextAction: UIAlertAction = UIAlertAction(title: "Next", style: .Default) { action -> Void in
let mainStoryboard = UIStoryboard(name: "Storyboard", bundle: NSBundle.mainBundle())
let vc : UIViewController = mainStoryboard.instantiateViewControllerWithIdentifier("vcMainLogin") as UIViewController
self.presentViewController(vc, animated: true, completion: nil)
}
myAlertController.addAction(nextAction)
//Present the AlertController
self.presentViewController(myAlertController, animated: true, completion: nil)
}

How do we create and dismiss an UIAlertController without user input? (Swift)

I've been looking up a lot of tutorials on UIAlertController. Thus far, the way I found was to activate a UIAlertController by linking it to a button or label and then call a IBAction.
I tried to replicate the code to automatically pop an alert when user enters the app (I wanted to ask the user if they want to go through the tutorial). However, I keep getting the error:
Warning: Attempt to present UIAlertController on MainViewController whose view is not in the window hierarchy!
Then I tried to add the UIAlertController to the MainViewController via addChildViewController and addSubview. However, I get the error:
Application tried to present modally an active controller
I figured that I cannot use the presentViewController function and commented it out.
The UIAlertController is displayed BUT when I tried to click on the cancel or the never button, this error occurs.
Trying to dismiss UIAlertController with unknown presenter.
I am really stumped. Can someone share what I am doing wrong? Thank you so much. Here is the code.
func displayTutorial() {
alertController = UIAlertController(title: NSLocalizedString("tutorialAlert", comment: ""), message: NSLocalizedString("tutorialMsg", comment: ""), preferredStyle: .ActionSheet)
self.addChildViewController(alertController)
self.view.addSubview(alertController.view)
alertController.didMoveToParentViewController(self)
alertController.view.frame.origin.x = self.view.frame.midX
alertController.view.frame.origin.y = self.view.frame.midY
//alertController.popoverPresentationController?.sourceView = self.view*/
let OkAction = UIAlertAction(title: NSLocalizedString("yesh", comment: ""), style: .Destructive) { (action) in
}
alertController.addAction(OkAction)
let cancelAction = UIAlertAction(title: NSLocalizedString("notNow", comment: ""), style: .Destructive) { (action) in
//println(action)
self.tutorial = 1
self.presentedViewController?.dismissViewControllerAnimated(true, completion: nil)
}
alertController.addAction(cancelAction)
let neverAction = UIAlertAction(title: NSLocalizedString("never", comment: ""), style: .Cancel) { (action) in
self.tutorial = 1
}
alertController.addAction(neverAction)
//self.presentViewController(alertController, animated: false) {}
}
I found the solution. Apparently, I cannot call the UIAlertController from the func viewDidLoad. I must call the function from viewDidAppear. So my code now is
override func viewDidAppear(animated: Bool) {
if tutorial == 0 {
displayTutorial(self.view)
}
}
func displayTutorial(sender:AnyObject) {
let alertController = UIAlertController(title: NSLocalizedString("tutorialAlert", comment: ""), message: NSLocalizedString("tutorialMsg", comment: ""), preferredStyle: .ActionSheet)
let OkAction = UIAlertAction(title: NSLocalizedString("yesh", comment: ""), style: .Destructive) { (action) in
}
alertController.addAction(OkAction)
let cancelAction = UIAlertAction(title: NSLocalizedString("notNow", comment: ""), style: .Default) { (action) in
//println(action)
self.tutorial = 1
self.presentedViewController?.dismissViewControllerAnimated(true, completion: nil)
}
alertController.addAction(cancelAction)
let neverAction = UIAlertAction(title: NSLocalizedString("never", comment: ""), style: .Cancel) { (action) in
self.tutorial = 1
}
alertController.addAction(neverAction)
self.presentViewController(alertController, animated: true, completion: nil)
if let pop = alertController.popoverPresentationController {
let v = sender as UIView
pop.sourceView = view
pop.sourceRect = v.bounds
}
}
Thanks to this posting: Warning: Attempt to present * on * whose view is not in the window hierarchy - swift
Below UIAlertController with extension would help you show alert with dynamic number of buttons with completion handler for selected index
extension UIViewController {
func displayAlertWith(message:String) {
displayAlertWith(message: message, buttons: ["Dismiss"]) { (index) in
}
}
func displayAlertWith(message:String, buttons:[String], completion:((_ index:Int) -> Void)!) -> Void {
displayAlertWithTitleFromVC(vc: self, title: Bundle.main.infoDictionary!["CFBundleDisplayName"] as! String, andMessage: message, buttons: buttons, completion: completion)
}
func displayAlertWithTitleFromVC(vc:UIViewController, title:String, andMessage message:String, buttons:[String], completion:((_ index:Int) -> Void)!) -> Void {
let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
for index in 0..<buttons.count {
let action = UIAlertAction(title: buttons[index], style: .default, handler: {
(alert: UIAlertAction!) in
if(completion != nil){
completion(index)
}
})
alertController.addAction(action)
}
DispatchQueue.main.async {
vc.present(alertController, animated: true, completion: nil)
}
}
}
If you need to auto dismiss the alert you can call dismiss on presented view controller after some delay.
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 1) {
vc.dismiss(animated: true, completion: nil)
}
Hope this might help you.