How do you make a modal view appear outside of a controller? - swift

I am trying to program a little game just to apply the concepts I learned in this course myself. When the game opens up, I would like for a custom modal view to tell the user how to play. Likewise, when they lose, I want to present a results page, which would be inside an if statement. I've searched all over the internet, and I can't find a way to display these views without an error. All that this video shows is how to show a display a view when a button is pressed; how do I display a custom modal view on command in code? (I am very new to Swift, so try to put it in layman's terms.) Thanks!
import UIKit
var numberValue = 0
let randomInt = getRandomNumber()
class ViewController: UIViewController {
#IBOutlet weak var buttonLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
let vc = self.storyboard?.instantiateViewControllerWithIdentifier("HowToPlayView") as! HowToPlay
self.presentViewController(vc, animated: true, completion: nil)
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func buttonPressed() {
numberValue += 1
updateButtonValue(buttonLabel)
}
}
#IBAction func buttonPressed() {
numberValue += 1
updateButtonValue(buttonLabel)
}
}
That's my view controller. Getting error: Thread 1: EXC_BAD_ACCESS
My view in interface and connections
enter image description here

Ok, let's try a different approach to solve this issue. Above your ViewController you should see three different buttons like this:
Click on the first button and Ctrl + Drag to the view that you want to display and choose display modally. After that is done you should see this arrow appear:
This is called the segue from the first view to the second view and you can call this in your code to transition from one view to the next, but first we have to give our segue an identifier. You can do this by clicking on the arrow and a window should come up like this:
As you can see you will have to fill out the identifier field, and in my case I just named it "myAwesomeSegue".
Lastly, in the first ViewController run this code when you need to present the next view controller:
performSegueWithIdentifier("myAwesomeSegue", sender: nil)
Please note that if you use this method just delete the previous code you had before as this is a brand new approach (so basically delete this code):
let vc = self.storyboard?.instantiateViewControllerWithIdentifier("HowToPlayView") as! HowToPlay
self.presentViewController(vc, animated: true, completion: nil)
Final code for the firstViewController:

Related

Close a modal view with a button in Swift?

Learning some view controller basics and am stuck on how to dismiss a modal with a button.
In my slimmed-down example, I have a two view setup with the initial view and the modal. The first view has a button that successfully pops up the modal. On the modal, there is a button that should dismiss itself.
According to other posts and documentation, I should be able to run simple code attached to the button like this:
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func CloseModal(_ sender: Any) {
self.dismiss(animated: true, completion: nil)
}
}
When I tap the "Close Modal" button nothing happens. What am I missing here? Am I putting this code in the wrong place? Currently, it's in the main ViewController.swift file.
The other approach is to use an unwind segue to your main view controller. Just add an “unwind action” in the first view controller:
class ViewController: UIViewController {
#IBAction func unwindHome(_ segue: UIStoryboardSegue) {
// this is intentionally blank
}
}
Just give this unwind action a meaningful name, unwindHome in this example, so that, if and when you have multiple unwind actions later on, you can easily differentiate between them.
Then you can control-drag from the button in the second scene to the “exit” control and select the appropriate unwind action:
This has a few nice aspects:
The “close” button no longer cares how you presented it, it will unwind however is appropriate (e.g. if you later change the initial segue to be a show/push segue, the unwind segue will still work without any code changes).
Because you’re now using a segue to unwind, the presented view controller can use its prepare(for:sender:) to send data back, should you eventually need to do that.
If you want, you can unwind multiple scenes. For example if A presents B, and B presents C, you can obviously unwind from C to B, but you can also unwind all the way from C to A if you want.
So, while dismiss works, unwind segues are another alternative.
You actually have two ViewController screens, but it looks like you have one ViewController class? And is the 2nd screen connected to a class?
Must be in the class that belongs to the second screen of the closeModal method.
//This is First ViewController, OpenModal Button is here
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
}
// The name of the class and the Viewcontroller in the storyboard have to be the same, and CloseModol Button and function need to be here
class SecondViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func CloseModal(_ sender: Any) {
self.dismiss(animated: true, completion: nil)
}
}
Don't forget to set the name of the ViewController in Storyboad;
from FirstViewController to SecondViewController

Present modal from NSWindowController without using Storyboards

I building a macOS framework that's running at the beginning of the login process and I want to show some information to the user in a window. I have an NSWindowController with a .XIB file and it's the main window for my framework.
I would like to add more presentAsSheet to another view. For that, I created an NSViewController with a .XIB to define the user interface that an I want to present in a modal but here I'm completely blocked because getting the contentViewController of the window its always nil
I'm initializing my main window like this:
func run() {
NSApp.activate(ignoringOtherApps: true)
mainWC = MainWC(windowNibName: "MainWC")
guard mainWC.window != nil else {
return
}
NSApp.runModal(for: mainWC.window!)
}
Where Im trying to present the other NSViewController"
self.window?.contentViewController?.presentAsSheet(customViewController)
In my MainWindowController I'm doing this:
#IBAction func btnAction(_ sender: Any) {
let customModal = CustomModal(windowNibName: "CustomModal")
self.window?.beginSheet(customModal.window!, completionHandler: { code in
print(message: "Clicked")
})
}
The modal never appears
You can't present the view until the window and the content view have been loaded
Either inside the window controller in windowDidLoad()
override func windowDidLoad() {
super.windowDidLoad()
// present the view
}
or even in viewDidLoad of the view controller
override func viewDidLoad() {
super.viewDidLoad()
// present the view
}

How can I dismiss the parent view controller after the second view controller is launched?

I am working on an OSX app in swift which programmatically clicks a button to launch a view controller. I would like to dismiss the initial view controller programatically.
class ViewController: NSViewController {
#IBOutlet weak var button: NSButton!
override func viewDidLoad() {
super.viewDidLoad()
button.performClick(nil)
self.dismissViewController(self)
// Do any additional setup after loading the view.
}
override var representedObject: AnyObject? {
didSet {
// Update the view, if already loaded.
}
}
}
Just to upgrade the previous answer
self.view.window?.close() doesn't work in swift 4 you might want to try the code below.
self.view.window?.rootViewController?.dismiss(animated: false, completion: nil)
It looks like your button isn't actually performing dismissViewController(). You're actually calling this method in your viewDidLoad(). It's also worth mentioning that you might want to try self.view.window?.close() instead.

Data between view controllers not passing

I am creating an application in which there are 6 view controller in storyboard. The thing is that data is shared between the default view controller and the first one ( say A and B) which i added. i am using the prepareforseque method for passing data. the problem started when i added two more view controller. lets say C and D i created two new swift files and changed the two view controller class name. i created a textbox and button in C and label in D. when i pressed the button, the value of the text field is not passing into the D view controller although i used the same methods and code which i used for A and B. do i have to do anything else when i want to pass data between two newly added view controller.
first viewcontroller in which when a button is pressed value 1 needed to be passed:
class PlaySelectMenu: UIViewController {
var value = Int()
#IBAction func twotofive(sender: AnyObject) {
value = 1
}
override func viewDidLoad() {
super.viewDidLoad()
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let nextView : PlayGameView = segue.destinationViewController as! PlayGameView
nextView.x = value
}
}
the second view controller which receive the value and print it
import Foundation
import UIKit
class PlayGameView: UIViewController{
var x = Int()
override func viewDidLoad() {
super.viewDidLoad()
print(x)
}
}
here i have added both the view controller from the object library and not working with the default one which is present in storyboard by default. i dont know why these two viewcontroller are not working. please help.
Regards Dev
One solution would be to write the data out to NSUserDefaults and then read it back from NSUserDefaults in the other view controller. Probably not the proper or correct way to share data between two view controllers, but it's been a reliable work around for me.
Other than that, you'd need to share your code so that we can see what's occurring.
Can you post also the code in your controllers C & D. And also if you have copy/paste the code inside your first two controllers into the two others, are you sure that in your prepareForSegue method you have changed the name of the destination segue ?
Assuming you have created the segue in Storyboard:
All you need is to do is put all of needed updates in prepareForSegue because twotofive is called after prepareForSegue.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
value = 1
let nextView : PlayGameView = segue.destinationViewController as! PlayGameView
nextView.x = value
}
Since you have connected your segue from button click to view controller, when you press button segue is automatically called. Instead of connecting segue from button to VC, connect VC to VC. Then in button click method at the last add below line:
#IBAction func twotofive(sender: AnyObject) {
value = 1
self.performSegueWithIdentifier("<Name of the segue identifier>", sender: self)
}
This will call your prepareForSegue. If you are calling more then one VC using segue from a VC then you can use segue.identifier to check which VC was called as below
override func prepareForSegue(segue: UIStoryboardSegue!, sender: AnyObject!) {
if segue.identifier == "CVC" {
}

How to pass a string between viewcontrollers with out the use of a SEGUE

Recently I have been making a app where you can create and quiz yourself on definitions or anything for that matter. I pass data to the next view after it the user hits the create button to make the title of the new notecard. The code I am using right now for that is:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let DestViewController: Card1 = segue.destinationViewController as! Card1
DestViewController.Content = Notetitle.text!
self.saved = self.Notetitle.text!
}
All of that works but, it will only work if I have a segue between viewcontrollers. I need to be able to pass that data with out a segue because I want the user to be able to create as many notecards as they want and the way I am trying to do that now is by using this code to make a copy of the UIView and then put in the new data (a master view). The new view can only be create using an IBAction. The prepare for segue I cannot use in the IBAction because it is it's own override function.
This is the code I am using to make a the new view:
let newCard =
self.storyboard!.instantiateViewControllerWithIdentifier("Main")
self.presentViewController(newCard, animated: true, completion:nil)
My hope is that I will be able to make a new view and then pass in the data pass in the data that the user just made to go on the notecard. (Hope this makes any sense at all)
MAIN TOPICS: -Create a new view and pass in new data Problem: Can pass data without a segue dont have one :/ -Be able to pass data between view controllers without a segue :)
I am new to all of this about 5 months. All of my code is in swift. Take it easy on me please. Feel free to ask me with any questions or comments. I have already posted a question on this but I didnt get an answer so have at it.
Thanks, Lucas Mazza
Don't use global variables unless you really need to. Making global static singleton's does not follow best practices. For more information read: What is so bad about singletons?
A better solution
You can use the protocol delegate pattern. I've actually written an article on this topic here:
https://www.codebeaulieu.com/36/Passing-data-with-the-protocol-delegate-pattern
You'll need a protocol that defines a function that will accept data. Then your other view controller will need to implement the delegate. If you need step-by-step details see the link provided above, alternatively you can simply download the project below and examine the code.
Download Working Example Project
Here's the code to make your protocol-delegate pattern work:
View Controller 1:
class ViewController: UIViewController, PresentedViewControllerDelegate {
#IBOutlet weak var textOutlet: UILabel!
#IBAction func doPresent(sender: AnyObject) {
let pvc = storyboard?.instantiateViewControllerWithIdentifier("PresentedViewController") as! PresentedViewController
pvc.data = "important data sent via delegate!"
pvc.delegate = self
self.presentViewController(pvc, animated: true, completion: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func acceptData(data: AnyObject!) {
self.textOutlet.text = "\(data!)"
}
}
View Controller 2:
import UIKit
// place the protocol in the view controller that is being presented
protocol PresentedViewControllerDelegate {
func acceptData(data: AnyObject!)
}
class PresentedViewController: UIViewController {
// create a variable that will recieve / send messages
// between the view controllers.
var delegate : PresentedViewControllerDelegate?
// another data outlet
var data : AnyObject?
#IBOutlet weak var textFieldOutlet: UITextField!
#IBAction func doDismiss(sender: AnyObject) {
if textFieldOutlet.text != "" {
self.presentingViewController!.dismissViewControllerAnimated(true, completion: nil)
}
}
override func viewDidLoad() {
super.viewDidLoad()
print("\(data!)")
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(animated)
if self.isBeingDismissed() {
self.delegate?.acceptData(textFieldOutlet.text)
}
}
}