For example if i have:
class SpriteKitScene: SKScene {
...
}
And in there i want to have an image, that when tapped(pressed, clicked, touched whatever)
loads another file with:
class UiViewcontrollerScene: UIViewcontroller {
...
}
I know how to transition from SKScene to SKScene, but i need to transition from SKScene to UIViewcontroller.
First, set yourself up a delegate using a protocol for your view controller.
protocol UIViewControllerDelegate{
}
See here: https://makeapppie.com/2014/07/01/swift-swift-using-segues-and-delegates-in-navigation-controllers-part-1-the-template/ for a nice tutorial on how to do that
Create an SKView class that will be hosting this delegate
class GameView : SKView
{
var delegate : UIViewControllerDelegate?
}
Then on your viewDidLoad in your UIViewController class, assign the delegate to your view controller.
override func viewDidLoad()
{
if let view = self.view as? GameView
{
view.delegate = self
}
}
Now your view has a delegate to your view controller, From this point, in your protocol file, make a method to transition
E.G.
protocol UIViewControllerDelegate
{
optional func transitionToMenuVC()
}
Then apply the code to your view controller class.
class ViewController : UIViewController, UIViewControllerDelegate
{
...
func transitionToMenuVC()
{
// do transition code here
}
}
Now you have it all set up for your view to communicate with your view controller.
In your Scene, you would just cast the scene's view to the GameView, and use the delegate to transition
class GameScene : SKScene
{
...
func transition()
{
if let view = self.view as? GameView
{
view.delegate.transitionToMenuVC()
}
}
}
Do note however, it is impossible to transition from scene to view controller, because they are 2 different animals. You will be transitioning the views, and are therefor stuck using the animations provided for views.
Related
I have some buttons in Child View Controller. I'd like a user to be able to click on these buttons, and, in case he clicks in other area, Parent View Controller to be able to process those clicks that Child View Controller doesn't work with.
You need to create protocols and delegates.
protocol Vc2Delegate : AnyObject {
func buttonPressed()
}
class Vc1 : UIViewController, Vc2Delegate {
func presentVC2() {
let vc2 = Vc2()
vc2.delegate = self
present(vc2)
}
func buttonPressed() {
// process vc2 button press in vc1
}
}
class Vc2 : UIViewConttoller {
// weak !!!!
weak var delegate : Vc2Delegate? = nil
func buttonInVc2Pressed() {
// call func in vc1 from vc2
delegate?.buttonPressed()
}
}
P.S. Sorry for formatting, was wrote from iPhone.
When testing my app I realised that going to the view controller which presents my scene and back again increases the used space in memory. So I run instruments to learn what wasn’t deallocated. I found that there was some instances of my scene taking up space. How can I fix it?
I have a property in my scene class to access its view controller (which I defined as weak to avoid a retain cycle)
The concerning lines in my scene class are:
weak var viewController: MyViewControllerClass?
func exitFunction () {
viewController.dismiss(animated: true , completion: nil)
self.view!.presentScene(nil)
}
All your scenes should belong to the same view controller, that is to say in a sprite kit game there is only one view controller.
You should define your scenes and the SKView to present them on in this view controller like so:
class GameViewController: UIViewController {
var skView: SKView?
var mainMenu: MainMenu?
override func viewDidLoad() {
super.viewDidLoad()
skView = self.view as! SKView?
}
Inside your scene make sure you are deallocating your resources properly:
override func willMove(from view: SKView) {
removeAllChildren()
}
deinit {
//Deallocate your resources here
}
WillMove is called each time you move from the scene to another.
Finally when you show your scenes from inside your viewController class make sure you deallocate old ones first:
func showMainMenu(_ notification: Notification) {
deallocScenes()
mainMenu = MainMenu(fileNamed: "MainMenu")
mainMenu?.scaleMode = .fill
skView!.presentScene(mainMenu)
}
func deallocScenes(){
mainMenu = nil
levelSelection = nil
credits = nil
}
I have the following setup:
StartViewController has a ContainerView that contains ContainerViewController
I try to find a way to hidden an element in StartViewController after a task is performed in ContainerViewController.
For this I try to use delegation method like this:
StartViewController
class StartViewController: UIViewController, showBannerAdDelegate {
#IBOutlet weak var bannerView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
bannerView.hidden = false
}
func bannerAdHidden(status: Bool) {
bannerView.hidden = status
}
}
ContainerViewController
protocol showBannerAdDelegate: class {
func bannerAdHidden(status: Bool)
}
class ContainerViewController: UIViewController {
weak var delegate: showBannerAdDelegate! = nil
#IBAction func buttonPressed(sender: UIButton) {
delegate.bannerAdHidden(true)
}
}
If I presented the ContainerViewController I could do in prepareForSegue
let destination = segue.destinationViewController as! ContainerViewController
destination.delegate = self
But in this case both View Controller are always present.
What code should I add to the View Controller to make it work?
Thank you,
If one of the view controllers is inside a container view then it is loaded with an embed segue, which fires when the containing view controller is first loaded. The prepareForSegue method still gets called, so you can set up a delegate exactly as you've described. I always thought embed segues were a little odd (it's not really a segue, more like loading a child view controller) but that's how it works.
There seems to be so many contradicting ideas on this topic.
I simply wish to have my menu in a UIViewController and my game in the SKScene
In my SKScene I used:
self.removeFromParent()
self.view?.presentScene(nil)
The nodes are removed but the scene is still in place as I still have the grey background and fps counter. Can I return to the View aspect of the UIViewController and hide the scene?
My method one implementation:
RootViewController:
class RootViewController: UIViewController {
var menu = MenuViewController()
var game = GameViewController()
override func viewDidLoad() {
super.viewDidLoad()
print("root")
MenuPresent()
}
func GamePresent() {
self.addChildViewController(game)
self.view.addSubview((game.view)!)
game.didMoveToParentViewController(self)
}
func MenuPresent() {
self.addChildViewController(menu)
self.view.addSubview((menu.view)!)
menu.didMoveToParentViewController(self)
}
func menuDismiss() {
menu.willMoveToParentViewController(nil)
menu.removeFromParentViewController()
menu.view.removeFromSuperview()
}
}
MenuViewController:
class MenuViewController: UIViewController {
//var root = RootViewController()
override func viewDidLoad() {
super.viewDidLoad()
print("menu")
}
}
The print("menu") appears in my console, But the actual view and all assets of the MenuViewController do no appear.
On the other hand my GameViewController and it's SKScene work fine.
Views and scenes are two different things. Scenes are held inside of a view. You would have to simply present a scene within your menu view or transition to another view and present an SKScene from there. The code to present the scene might look like this:
let scene = GameScene(fileNamed: "GameScene")
let skView = self.view as! SKView
scene?.scaleMode = .AspectFit
skView.presentScene(scene)
First you need to know that SKScene and UIViewController are totally two different things. The hierarchy is typically as following:
UIViewController --> UIView(SKView) --> SKScene
So you SKScene is presented in a SKView which can be a UIView, then the UIView is presented in a UIViewController.
Once you know the hierarchy, everything is easy. There are many ways to use different UIViewController for menu and GameScene stuff.
Method One
For example, you can have a RootViewController, a GameViewController and a MenuViewController. The RootViewController is the initial ViewController when the app launches.
In the RootViewController, you can create a function to present the GameViewController:
func setupGameViewController() {
self.gameViewController = GameViewController()
self.addChildViewController(gameViewController!)
self.view.addSubview((gameViewController!.view)!)
gameViewController?.didMoveToParentViewController(self)
}
You need to present you SKScene in GameViewController, I guess you should be familiar with this step.
Then when you need to display the menu, you can add the MenuViewController to RootViewController with a function like:
func setupMenuViewController() {
self.menuViewController = MenuViewController()
self.addChildViewController(menuViewController!)
self.view.addSubview((menuViewController!.view)!)
menuViewController?.didMoveToParentViewController(self)
}
You also need to present you menuView in this ViewController, which I suppose you already know.
Also create a function to dismiss the MenuViewController:
func removeMenuViewController(){
self.menuViewController?.willMoveToParentViewController(nil)
self.menuViewController?.removeFromParentViewController()
self.menuViewController?.view.removeFromSuperview()
}
And everything is done.
Method Two
You can also have only one UIViewController, but create you menu as a UIView, then you can use self.view.addSuview(menuView) to present your menu. Of course you root view, which is the self.view as SKView is still there, but it doesn't matter because it's hidden behind the menuView.
A Note for your updated question
You cannot remove the scene because the scene is actually self.view as SKView, self.view is the root view of a UIViewController, it cannot be removed. If you really want to remove you scene (for most cases, it's not necessary), you can create a new SKView that present your SKScene, then add this SKView to your UIViewController by self.view.addSubview(skView), when you want to completely remove the scene, just use skView.removeFromSuperview().
I have a root VC that embeds a table view through a container view with segue. So that makes both the root VC and child VC visible at the same time.
if segue.identifier == "TableSegue" {
let toView = segue.destinationViewController as! TableViewController
toView.delegate = self
}
How can I implement a protocol delegate between the root vc and child vc since the child VC is embedded inside the root vc?
What I want to do is to have a functions fired in the child VC once a button in root VC is clicked.
I have tried to implement a protocol delegate the normal way but it seems to not be picked up in the child VC
protocol TableViewInterface {
func someWork()
}
class RootVC:UIViewController {
var delegate: TableViewInterface?
func callDelegate() {
delegate?.someWork()
}
}
class TableViewController: UITableViewController, TableViewInterface {
func someWork() {
//Perform your the work you want done or the action you want fired in your tableView here...
}
}
The above is an example of a standard delegate pattern in swift. It looks like you are trying to set the delegate in prepareForSegue(), but IRRC it won't get called with a containerView. You probably want to get a reference to you tableView through your container view.
You should be able to get a reference to it by doing something like this in your RootVC
if let tableVC = childViewControllers[0] as? TableViewController {
self.tableViewController = tableVC
}
I hope this all makes sense