I'm developing a SpriteKit game with two different scenes (MainMenu.sks and Settings.sks). I also have a UIViewController.
From the MainMenu the user can tap "Settings" which causes the app to load the Settings.sks scene and present it.
It's done using this code:
// Move to Settings.sks
let transition = SKTransition.flipVertical(withDuration: 1.0)
let loadScene = SettingsScene(fileNamed: "SettingsScene")
loadScene?.scaleMode = self.scaleMode
self.view?.presentScene(loadScene!, transition: transition)
From the Settings.sks scene the user can tap on the SKLabelNode "Change username" which causes the app to load the UIViewController by performing a segue.
The UIViewController contains a UITextField and a UIButton. The UIButton takes the user back to MainMenu.sks.
However, when I'm doing so the it's like a new scene of MainMenu is placed upon the other causing the app to sort of "freeze" and not responding to any touch gestures. I can see from the nodeCount that the nodes are doubled.
A workaround is loading the UIViewController directly from the MainMenu.sks (also via a segue). By doing so, the app works just fine.
I'm suspecting the problem to be the way I load Settings.sks.
What am I missing? Maybe unloading the entire SKView before exiting?
Performing a segue will present a new view on top of the stack. By using a segue to return to the main menu, the app will create a new main menu on top of the UIViewController. I can't say for certain why your app freezes. But I would guess that there are some properties or functions that don't work properly when a new main menu is created and presented from your UIViewController.
I recommend unwinding the segue used to reach the UIViewController instead. This way you can return to the previous view and conserve memory.
You'll need to write an IBAction in your destination view, the main menu in this case:
#IBAction func unwindToMainMenu(segue: UIStoryboardSegue) {
}
Then ctrl-drag from your button in the UIViewController to the exit icon in storyboard. You should see an option to use the IBAction unwindToMainMenu that you just wrote. This will create an unwind segue.
A complete guide to unwind segues and multiple examples can be found in this answer: What are Unwind segues for and how do you use them?
Related
I have the following setup:
A UIPageviewController that has two child-ViewControllers.
ViewController 1 contains a UITableView, ViewController 2 holds a camera setup (AVCaptureSession, etc). It's basically a Snapchat like setup.
Swiping between the two works perfectly - swipe left, UITableView is showing, swipe right and the camera shows up again. So far so good.
Now I want to implement a button that turns the page to get to the controller that hold the UITableView programatically. Here's how I do it:
let startingViewController = self.viewControllerAtIndex(0)
let viewControllers: [UIViewController] = [startingViewController!]
pageViewController!.setViewControllers(viewControllers, direction: .Reverse, animated: true, completion: nil)
This works for the most part, as it slides out the UIViewController holding the camera instance and slides in the one holding the UITableView. However, it seems like the view controller that holds the camera session is deallocated, so when I swipe right again, the camera has to be fired up again. Is there any way to turn the page differently, without having the camera session deallocated?
I have tried adding the camera to the PageViewcontroller and adding an "invisible" overlay, but this is not the solution I am looking for. Can someone help out here?
The correct way to do this is using a UIScrollView with multiple subviews and paging enabled. When you press the button, you do not "turn the page", you manipulate/animate the content of the scroll view.
For further reference:
https://developer.apple.com/library/ios/documentation/WindowsViews/Conceptual/UIScrollView_pg/ScrollViewPagingMode/ScrollViewPagingMode.html
There are couple for segues in the storyboard, all with identifier. Is it possible to access them through code and change its anchor?
Something like this:
mySegue1.anchor = someOtherView
try this
self.performSegueWithIdentifier("YourSegueIdentifier", sender: self)
I think you may chnage anchor's view controller with the following codes, as I wrote in above comments, I believe you can create a Segue programmatically and the following code will create a new Segue for you not just change an old already drawn Segue's anchor programmatically.
Most of the good programmers think that you can not create a Segue by code, thats why changing a Segue anchor is not believed to be a performable job. But it is
It is an 'Objective C' version but you may easily convert it to 'Swift' if you like.
UIViewController *toViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"OtherViewControllerId"];
MySegue *segue = [[MySegue alloc] initWithIdentifier:#"" source:self destination:toViewController];
[self prepareForSegue:segue sender:sender];
[segue perform];
However if you need to create a popover menu using a gesture recognizer, then just create a gesture recognizer and activate your pop over in gesture's action func ( handleTap() )
take a look at the following link, you may create your popover in storyboard and then write the rest of the code or completely do it by code, both are fully covered here.
http://gracefullycoded.com/display-a-popover-in-swift/
as I understood which I hope I get it right, you are about to create a pop over on your view controller which has a tab gesture defined on it. and you succeed to do the job but the pop over menu is mislocated. if this is the case You dont need to use a segue or a storyboard, all parts can be done by code easily.
What you can do is making an invisible anchor frame (ie a label ) and using that as anchor for the segue. I had som buttons and use this approach and it works fine. If you replace my buttons with you dynamically created objects.
#IBAction func rearHubActionUIButton(sender: UIButton) {
invisibleAncherLabel.frame = sender.frame
performSegueWithIdentifier("PopSelection", sender: sender)
}
The popover will then appear with your object as anchor.
I have a tabbed application with three tabs and I want to make the transition between them with a Swipe Gesture Recogniser.
I dragged and dropped one Swipe Gesture Recogniser from the Object Library to the first View and one to the third View. I added two on the second View so I can swipe both left and right.
I changed the Swipe to the Attribute Inspector so it will swipe right or left as needed
Using the Assistant editor I used Control+Drag to create Actions to the proper ViewController.swift files.
What else should I do so the swiping should work?
All of the guides I found online are in Objective-C. Does anyone know how to complete these Events in Swift?
You have to apply custom view controller transitions. Otherwise your tab transition will be neither animated (by default tab switches are without animation) nor interactive (you cannot control the speed with your gesture).
A good starting point is Colin Eberhardt's VC Transitions Library. It's in Objective-C but you can use it from Swift.
Just a warning: Custom view controller transitions are an immature part of iOS 7 or 8. If another view transition is initiated while a custom one is still running, the UI will more or less lock up. And it easily happens: user presses yet another tab before the transitions ends, your code switches tab and pushes a view controller at the same time, user manages to press a button during transition that opens another view controller etc.
EDIT: I have added a sample project to github and it fully works. Feel free to copy it or wok on it.
You don't need to add them in the story board. First add this code to your initializer (in most cases viewDidLoad):
var swipeGesture = UISwipeGestureRecognizer(target: self, action: "SwipeToNextViewController:")
swipeGesture.direction = UISwipeGestureRecognizerDirection.Left
view.addGestureRecognizer(swipeGesture)
var swipeGestureReverse = UISwipeGestureRecognizer(target: self, action: "SwipeToPreviousViewController:")
swipeGestureReverse.direction = UISwipeGestureRecognizerDirection.Right
view.addGestureRecognizer(swipeGestureReverse)
Now that your viewController has the gesture, SwipeToNextViewController() will be called if you swipe left, and SwipeToPreviousViewController() will be called when swiped Right.
Write the code for the two functions associated with the gestures:
func SwipeToNextViewController(gestureRecognizer:UISwipeGestureRecognizer)
{
performSegueWithIdentifier("next", sender: self)
}
func SwipeToPreviousViewController(gestureRecognizer:UISwipeGestureRecognizer)
{
performSegueWithIdentifier("previous", sender: self)
}
These two functions will be called when view has been swiped left or right. Add your code in it (in this case performSegue) and you can segue to another viewController.
Hope it helps :)
One of my main reason for using Xcode instead of other applications for making iOS apps was the storyboard interface builder. I was unhappy when I found out that you are not meant to use storyboards with spriteKit. I find it hard to design a nice interface for a game's menu without a good visual builder. Is there a way to start a spriteKit application using storyboards, then with a click of a "start game" button, switch to spriteKit scenes, then when you lose the game, in the code switch back to storyboards(using swift)? Please help and thanks.
-Callum-
A SpriteKit Scene is presented on an instance of a SKView, which is a subclassed UIView.
For an iOS game made using SpriteKit, one needs to have at least one viewController set up, either programatically in the App delegate or in a storyboard, on which the SKScene can be displayed. It is on the main view of this VC that a SKScene will be presented in.
So if you are using a storyboard, the iOS game will have to instantiate the root viewController from it. You can easily design your UI on the viewController, and present the game from the code on the press of a button, either in the same viewController or a new one. All this will be evident once you read a beginner tutorial for SpriteKit using Swift like this.
Let's say your root viewController has your main menu (on another view called menuView), with a play button on it. Now presenting a game on the press of a button would look something like this:
class MyViewController: UIViewController {
#IBOutlet wear var menuView: UIView?
#IBOutlet weak var playButton: UIButton?
#IBAction func likedThis(sender: UIButton) {
//Hide the menu view
menuView.hidden = true
//instantiate and present the scene on the main view
let scene = MyScene(size: view.bounds.size)
let skView = self.view as SKView
skView.presentScene(scene)
}
}
As for going back to the main menu from the scene, have a look at this answer.
I am using XCode 6 beta 5 with Swift / Scenekit. I have a SCNNode (a 3D SCNText object) that I want to use as a button. How do I connect this to a segue? With storyboard or programmatically? I thought of connecting the code for the SCNNode to a hidden button on the story board which connects to the segue but that seemed kinda hackish.
This is the same case as performing a segue from a map annotation, dynamically choosing between multiple segues from the same table cell, performing a segue in response to SpriteKit event handling, or any other case where you need to perform a segue in response to something that happens in your code instead of in response to the user tapping a control you set up in IB. You need a "manual" segue.
In the storyboard, create a segue by dragging from the (source) view controller itself to the destination view controller (instead of by dragging from a button or other control). Define an identifier for the segue using the inspector pane.
When something happens in your code that should trigger the segue, call performSegueWithIdentifier: with the identifier you defined. (If this code is inside of the view controller class, you can call it on self. Otherwise you'll need a reference to the view controller to segue from.)
As #FlippinFun points out, you can find code for touch handling in the SceneKit Game Xcode template. (For those following along at home, the gist of it is to use the view's hitTest method to find the scene element at the touch/click location.) In there is where you'd call performSegueWithIdentifier.
But there's a problem -- that template puts its touch handling code inside a view class instead of in the view controller, so the touch handling code doesn't know about the current view controller. Some possible solutions, ranked from "Best MVC architecture" to "Most quick-and-dirty":
Instead of subclassing SCNView to implement touch handling code, use a UITapGestureRecognizer instance instead -- it can call an action method on your view controller, and then you can use the recognizer's locationInView method to get a point you can hitTest with. Since you're still in view controller code there, you can call self.performSegueWithIdentifier once you get a hit-test result you like.
In the SCNView subclass, keep a (weak!) reference to the view controller that owns it. Then the view's touch handling code can call performSegueWithIdentifier on that view controller. (If you want to keep things abstract so the view doesn't have to know the view controller class, define a protocol.)
In the SCNView subclass' touch handling code, use self.window.rootViewController to find the view controller, and cast the result to your view controller class.
As far as using the text as a button, load up a new project with the rotating cube. Inside of the handleTouches is exactly what you are looking for.