Swift Collision Boundary not working - ios

Swift 2.0, xcode 7, Ios 9
Intention: I want the two squares to drop to the bottom and stay at the bottom of the screen.
What is happening now: There is no error preventing the code from running, the gravity works fine but the collision just seems to be ignored. Thus resulting in the two objects falling through the bottom of the screen.
Note: the subclass is UIView not UIViewController and this view is accessed from a segue.
Many Thanks!
Code :
import UIKit
class graphs: UIView {
//create two shapes
var greenSquare: UIView?
var redSquare: UIView?
var animator: UIDynamicAnimator?
override func drawRect(rect: CGRect) {
var dimen = CGRectMake(25, 25, 60, 60)
greenSquare = UIView(frame: dimen)
greenSquare?.backgroundColor = UIColor.greenColor()
dimen = CGRectMake(130, 25, 90, 90)
redSquare = UIView(frame: dimen)
redSquare?.backgroundColor = UIColor.redColor()
//add them to the screen
self.addSubview(greenSquare!)
self.addSubview(redSquare!)
}
#IBAction func startbutton(sender: AnyObject) {
//initialise the animator
animator = UIDynamicAnimator()
//colision
let boundries = UICollisionBehavior(items:[greenSquare!,redSquare!])
boundries.translatesReferenceBoundsIntoBoundary = true
//add gravity
let gravity = UIGravityBehavior(items: [greenSquare!, redSquare!])
let direction = CGVectorMake(0.0, 1.0)
gravity.gravityDirection = direction
animator?.addBehavior(boundries)
animator?.addBehavior(gravity)
}
}

I fixed it by changing the subclass to a UIViewController
why did the previous code not work?
this is the new code:
import UIKit
class DrawView: UIViewController {
//Create two shapes
var greenSquare: UIView?
var redSquare: UIView?
var animator: UIDynamicAnimator?
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func startbutton(sender: UIButton) {
//Create the shapes
var dimen = CGRectMake(25, 25, 60, 60)
greenSquare = UIView(frame: dimen)
greenSquare?.backgroundColor = UIColor.greenColor()
dimen = CGRectMake(130, 25, 90, 90)
redSquare = UIView(frame: dimen)
redSquare?.backgroundColor = UIColor.redColor()
//Add them to the screen
self.view.addSubview(greenSquare!)
self.view.addSubview(redSquare!)
//Initialize the animator
animator = UIDynamicAnimator(referenceView: self.view)
//Gravity
let gravity = UIGravityBehavior(items: [greenSquare!, redSquare!] )
let direction = CGVectorMake(0.0, 1.0)
gravity.gravityDirection = direction
//Collision
let boundries = UICollisionBehavior(items: [greenSquare!, redSquare!] )
boundries.translatesReferenceBoundsIntoBoundary = true
//Elasticity
let bounce = UIDynamicItemBehavior(items: [greenSquare!, redSquare!] )
bounce.elasticity = 0.5
animator?.addBehavior(bounce)
animator?.addBehavior(boundries)
animator?.addBehavior(gravity)
}
}

Related

How to attach multiple UIDynamicItems to each other

I am trying to implement circles attached to each other like in Apple's Music App via UIDynamicAnimator. I need to attach circles to each other and to view center. I was trying to implement this via UIAttachmentBehavior, but seems to it's not supporting multiple attachments. In result, circles overlaps on each other :)
let attachment = UIAttachmentBehavior(item: circle, attachedToAnchor: CGPoint(x: view.center.x, y: view.center.y))
attachment.length = 10
animator?.addBehavior(attachment)
let push = UIPushBehavior(items: [circle], mode: .continuous)
collision.addItem(circle)
animator?.addBehavior(push)
What I am doing wrong?
I don't think the apple music genre picker thing uses UIAttachmentBehavior which is closer to attaching two views with a pole or a rope. But, it seems like the problem you're experiencing might be that all of the views are added at the same location which has the effect of placing them on top of each other and with the collision behavior causes them to be essentially be stuck together. One thing to do is to turn on UIDynamicAnimator debugging by calling animator.setValue(true, forKey: "debugEnabled").
For recreating the above circle picker design, I would look into using UIFieldBehavior.springField().
For example:
class ViewController: UIViewController {
lazy var animator: UIDynamicAnimator = {
let animator = UIDynamicAnimator(referenceView: view)
return animator
}()
lazy var collision: UICollisionBehavior = {
let collision = UICollisionBehavior()
collision.collisionMode = .items
return collision
}()
lazy var behavior: UIDynamicItemBehavior = {
let behavior = UIDynamicItemBehavior()
behavior.allowsRotation = false
behavior.elasticity = 0.5
behavior.resistance = 5.0
behavior.density = 0.01
return behavior
}()
lazy var gravity: UIFieldBehavior = {
let gravity = UIFieldBehavior.springField()
gravity.strength = 0.008
return gravity
}()
lazy var panGesture: UIPanGestureRecognizer = {
let panGesture = UIPanGestureRecognizer(target: self, action: #selector(self.didPan(_:)))
return panGesture
}()
var snaps = [UISnapBehavior]()
var circles = [CircleView]()
override func viewDidLoad() {
super.viewDidLoad()
view.addGestureRecognizer(panGesture)
animator.setValue(true, forKey: "debugEnabled")
addCircles()
addBehaviors()
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
gravity.position = view.center
snaps.forEach {
$0.snapPoint = view.center
}
}
func addCircles() {
(1...30).forEach { index in
let xIndex = index % 2
let yIndex: Int = index / 3
let circle = CircleView(frame: CGRect(origin: CGPoint(x: xIndex == 0 ? CGFloat.random(in: (-300.0 ... -100)) : CGFloat.random(in: (500 ... 800)), y: CGFloat(yIndex) * 200.0), size: CGSize(width: 100, height: 100)))
circle.backgroundColor = .red
circle.text = "\(index)"
circle.textAlignment = .center
view.addSubview(circle)
gravity.addItem(circle)
collision.addItem(circle)
behavior.addItem(circle)
circles.append(circle)
}
}
func addBehaviors() {
animator.addBehavior(collision)
animator.addBehavior(behavior)
animator.addBehavior(gravity)
}
#objc
private func didPan(_ sender: UIPanGestureRecognizer) {
let translation = sender.translation(in: sender.view)
switch sender.state {
case .began:
animator.removeAllBehaviors()
fallthrough
case .changed:
circles.forEach { $0.center = CGPoint(x: $0.center.x + translation.x, y: $0.center.y + translation.y)}
case .possible, .cancelled, .failed:
break
case .ended:
circles.forEach { $0.center = CGPoint(x: $0.center.x + translation.x, y: $0.center.y + translation.y)}
addBehaviors()
#unknown default:
break
}
sender.setTranslation(.zero, in: sender.view)
}
}
final class CircleView: UILabel {
override var collisionBoundsType: UIDynamicItemCollisionBoundsType {
return .ellipse
}
override func layoutSubviews() {
super.layoutSubviews()
layer.cornerRadius = bounds.height * 0.5
layer.masksToBounds = true
}
}
For more information I would watch What's New in UIKit Dynamics and Visual Effects from WWDC 2015

UIImages or UIViews are not colliding each other in iOS Swift?

I am trying to collide two UIViews each other for using pan gesture. Now i can able to drag one view but when dragging view to next view its collide each other. It goes behind other view.
Here is the screenshot result:
Here is the code I used to drag, pan and collide of physics property.
import UIKit
class ViewController: UIViewController {
var greenSquare: UIView?
var redSquare: UIView?
var animator: UIDynamicAnimator?
var snap: UISnapBehavior!
var panGesture = UIPanGestureRecognizer()
#IBAction func pan(_ sender: UIPanGestureRecognizer) {
let tapPoint: CGPoint = sender.location(in: view)
if (snap != nil) {
animator?.removeBehavior(snap)
}
snap = UISnapBehavior(item: greenSquare!, snapTo: tapPoint)
animator?.addBehavior(snap)
}
override func viewDidLoad() {
super.viewDidLoad()
var dimen = CGRect(x: 25, y: 25, width: 120, height: 120)
greenSquare = UIView(frame: dimen)
greenSquare?.backgroundColor = UIColor.green
dimen = CGRect(x: 130, y: 25, width: 100, height: 100)
redSquare = UIView(frame: dimen)
redSquare?.backgroundColor = UIColor.red
self.view.addSubview(greenSquare!)
self.view.addSubview(redSquare!)
animator = UIDynamicAnimator(referenceView: self.view)
let gravity = UIGravityBehavior(items: [greenSquare!, redSquare!])
let direction = CGVector(dx: 0.0, dy: 1.0)
gravity.gravityDirection = direction
let boundries = UICollisionBehavior(items: [greenSquare!, redSquare!])
boundries.translatesReferenceBoundsIntoBoundary = true
let bounce = UIDynamicItemBehavior(items: [greenSquare!, redSquare!])
bounce.elasticity = 0.5
animator?.addBehavior(bounce)
animator?.addBehavior(boundries)
animator?.addBehavior(gravity)
panGesture = UIPanGestureRecognizer(target: self, action:#selector(draggedView(sender:)))
greenSquare?.isUserInteractionEnabled = true
greenSquare?.addGestureRecognizer(panGesture)
}
#objc func draggedView(sender:UIPanGestureRecognizer){
let translation = sender.translation(in: self.view)
greenSquare?.center = CGPoint(x: (greenSquare?.center.x)! + translation.x, y: (greenSquare?.center.y)! + translation.y)
sender.setTranslation(CGPoint.zero, in: self.view)
}
}//class`
Anyone can help me out this.
The issue is that the animator only recognizes the collision at the origin of the greenSquare. In this case it is at the top of the screen. You can update the collision location on the animator after you move the greenSquare. Add animator?.updateItem(usingCurrentState: greenSquare!)
to the end of the draggedView method. You do not need to use UISnapBehavior for this so you can remove the IBAction func pan method at the top of your code. The updateItem(usingCurrentState:) call will reset where the collision barrier is when you are moving the view manually and it is not being done by the physics engine.

SpriteKit - safe area layout Iphone X

So, Currently I have a game that i’ve made in sprite kit and have used this way of fitting everything to the screen size:
buyButton = SKSpriteNode(texture: SKTexture(imageNamed: "BuyButton"), color: .clear, size: CGSize(width: frame.maxX / 2.9, height: frame.maxY / 10))
buyButton.position = CGPoint(x:-frame.maxX + frame.midX*2, y: -frame.maxY + frame.midY*1.655)
addChild(buyButton)
as you can see it uses the frame to calculate the width and height along with the position of the node on the scene, this works with all screen sizes that I have been using from the 6s to the 8 Plus.
but when it comes to the iPhone X there is a problem with it. it seems to stretch everything because of the screen size being bigger and oddly shaped as you can see below compared to the iPhone 8 Plus
I’ve been looking for solutions to this problem but none have really helped or just even make it worse and I couldn’t understand how to programmatically use the safe area layout in my sprite kit project like in this solution here
My Question is
How do I go about getting everything to fit in the iPhone X’s screen so that it fits and isn’t cutting off the score labels and stuff I have in the top right corners?
EDIT 2:
itemDescriptionLabel = UILabel(frame: CGRect(x: frame.maxX - 150, y: frame.maxY - 130 , width: frame.maxX / 0.7, height: frame.maxY / 3.5))
itemDescriptionLabel.font = UIFont(name: "SFCompactRounded-Light", size: 17)
itemDescriptionLabel.text = "This purchase stops all the popup ads that happen after a certain amount of time playing the game from showing up."
itemDescriptionLabel.numberOfLines = 4
itemDescriptionLabel.adjustsFontSizeToFitWidth = true
self.scene?.view?.addSubview(itemDescriptionLabel)
EDIT 3
EDIT 4
let safeAreaInsets = yourSpriteKitView.safeAreaInsets;
buyButton.position = CGPoint(x:safeAreaInsets.left - frame.maxX + frame.midX*2, y: safeAreaInsets.top - frame.maxY + frame.midY*1.655)
EDIT 5
screenWidth = self.view!.bounds.width
screenHeight = self.view!.bounds.height
coinScoreLabel = Score(num: 0,color: UIColor(red:1.0, green: 1.0, blue: 0.0, alpha: 1), size: 50, useFont: "SFCompactRounded-Heavy" ) // UIColor(red:1.00, green:0.81, blue:0.07, alpha:1.0)
coinScoreLabel.name = "coinScoreLabel"
coinScoreLabel.horizontalAlignmentMode = .right
//coinScoreLabel.verticalAlignmentMode = .top
coinScoreLabel.position = CGPoint(x: screenWidth / 2.02, y: inset.top - screenHeight / 2.02)
coinScoreLabel.zPosition = 750
addChild(coinScoreLabel)
I tried it on another SpriteNode that I have such as this one below, which it worked for I have no idea why the yellow label is doing this.
playButton = SKSpriteNode(texture: SKTexture(imageNamed: "GameOverMenuPlayButton"), color: .clear, size: CGSize(width: 120, height: 65))
playButton.position = CGPoint(x: inset.right - frame.midX, y: inset.bottom + frame.minY + playButton.size.height / 1.7)
playButton.zPosition = 1000
deathMenuNode.addChild(playButton)
It came out like this which is perfect:
Interesting question. The problem borns when we try to "transform" our view to SKView to create our game scene. Essentially, we could intercept that moment (using viewWillLayoutSubviews instead of viewDidLoad) and transform the view frame accordling with the safeAreaLayoutGuide layout frame property.
Some code as example:
GameViewController:
class GameViewController: UIViewController {
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
if #available(iOS 11.0, *), let view = self.view {
view.frame = self.view.safeAreaLayoutGuide.layoutFrame
}
guard let view = self.view as! SKView? else { return }
view.ignoresSiblingOrder = true
view.showsFPS = true
view.showsNodeCount = true
view.showsPhysics = true
view.showsDrawCount = true
let scene = GameScene(size:view.bounds.size)
scene.scaleMode = .aspectFill
view.presentScene(scene)
}
override func viewDidLoad() {
super.viewDidLoad()
print("---")
print("∙ \(type(of: self))")
print("---")
}
}
GameScene:
class GameScene: SKScene {
override func didMove(to view: SKView) {
print("---")
print("∙ \(type(of: self))")
print("---")
let labLeftTop = SKLabelNode.init(text: "LEFTTOP")
labLeftTop.horizontalAlignmentMode = .left
labLeftTop.verticalAlignmentMode = .top
let labRightTop = SKLabelNode.init(text: "RIGHTTOP")
labRightTop.horizontalAlignmentMode = .right
labRightTop.verticalAlignmentMode = .top
let labLeftBottom = SKLabelNode.init(text: "LEFTBTM")
labLeftBottom.horizontalAlignmentMode = .left
labLeftBottom.verticalAlignmentMode = .bottom
let labRightBottom = SKLabelNode.init(text: "RIGHTBTM")
labRightBottom.horizontalAlignmentMode = .right
labRightBottom.verticalAlignmentMode = .bottom
self.addChild(labLeftTop)
self.addChild(labRightTop)
self.addChild(labLeftBottom)
self.addChild(labRightBottom)
labLeftTop.position = CGPoint(x:0,y:self.frame.height)
labRightTop.position = CGPoint(x:self.frame.width,y:self.frame.height)
labLeftBottom.position = CGPoint(x:0,y:0)
labRightBottom.position = CGPoint(x:self.frame.width,y:0)
}
}
Other way to do:
Another way to obtain just only the differences between the safe area layout frame and the current view frame without passing through viewWillLayoutSubviews for launching our scene, could be using protocols.
P.S.: Down below, I've report a reference image where you can see the different sizes between:
the main screen height (our view height)
safe area
status bar height (on the top, 44 px for the iPhone X)
GameViewController:
protocol LayoutSubviewDelegate: class {
func safeAreaUpdated()
}
class GameViewController: UIViewController {
weak var layoutSubviewDelegate:LayoutSubviewDelegate?
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
if let _ = self.view {
layoutSubviewDelegate?.safeAreaUpdated()
}
}
override func viewDidLoad() {
super.viewDidLoad()
print("---")
print("∙ \(type(of: self))")
print("---")
guard let view = self.view as! SKView? else { return }
view.ignoresSiblingOrder = true
view.showsFPS = true
view.showsNodeCount = true
view.showsPhysics = true
view.showsDrawCount = true
let scene = GameScene(size:view.bounds.size)
scene.scaleMode = .aspectFill
view.presentScene(scene)
}
}
GameScene:
class GameScene: SKScene,LayoutSubviewDelegate {
var labLeftTop:SKLabelNode!
var labRightTop:SKLabelNode!
var labLeftBottom:SKLabelNode!
var labRightBottom:SKLabelNode!
func safeAreaUpdated() {
if let view = self.view {
let top = view.bounds.height-(view.bounds.height-view.safeAreaLayoutGuide.layoutFrame.height)+UIApplication.shared.statusBarFrame.height
let bottom = view.bounds.height - view.safeAreaLayoutGuide.layoutFrame.height-UIApplication.shared.statusBarFrame.height
refreshPositions(top: top, bottom: bottom)
}
}
override func didMove(to view: SKView) {
print("---")
print("∙ \(type(of: self))")
print("---")
if let view = self.view, let controller = view.next, controller is GameViewController {
(controller as! GameViewController).layoutSubviewDelegate = self
}
labLeftTop = SKLabelNode.init(text: "LEFTTOP")
labLeftTop.horizontalAlignmentMode = .left
labLeftTop.verticalAlignmentMode = .top
labRightTop = SKLabelNode.init(text: "RIGHTTOP")
labRightTop.horizontalAlignmentMode = .right
labRightTop.verticalAlignmentMode = .top
labLeftBottom = SKLabelNode.init(text: "LEFTBTM")
labLeftBottom.horizontalAlignmentMode = .left
labLeftBottom.verticalAlignmentMode = .bottom
labRightBottom = SKLabelNode.init(text: "RIGHTBTM")
labRightBottom.horizontalAlignmentMode = .right
labRightBottom.verticalAlignmentMode = .bottom
self.addChild(labLeftTop)
self.addChild(labRightTop)
self.addChild(labLeftBottom)
self.addChild(labRightBottom)
labLeftTop.position = CGPoint(x:0,y:self.frame.height)
labRightTop.position = CGPoint(x:self.frame.width,y:self.frame.height)
labLeftBottom.position = CGPoint(x:0,y:0)
labRightBottom.position = CGPoint(x:self.frame.width,y:0)
}
func refreshPositions(top:CGFloat,bottom:CGFloat){
labLeftTop.position = CGPoint(x:0,y:top)
labRightTop.position = CGPoint(x:self.frame.width,y:top)
labLeftBottom.position = CGPoint(x:0,y:bottom)
labRightBottom.position = CGPoint(x:self.frame.width,y:bottom)
}
}
Output:
Subclassing methods:
Another way to get the correct safe area dimensions without touch your current classes code, could be made using some subclass, so for our GameViewController we set it as GameController and for GameScene we can set it as GenericScene like this example:
GameViewController:
class GameViewController: GameController {
override func viewDidLoad() {
super.viewDidLoad()
guard let view = self.view as! SKView? else { return }
view.ignoresSiblingOrder = true
let scene = GameScene(size:view.bounds.size)
scene.scaleMode = .aspectFill
view.presentScene(scene)
}
}
protocol LayoutSubviewDelegate: class {
func safeAreaUpdated()
}
class GameController:UIViewController {
weak var layoutSubviewDelegate:LayoutSubviewDelegate?
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
if let _ = self.view {
layoutSubviewDelegate?.safeAreaUpdated()
}
}
}
GameScene:
class GameScene: GenericScene {
override func safeAreaUpdated() {
super.safeAreaUpdated()
if let view = self.view {
let insets = view.safeAreaInsets
// launch your code to update positions here
}
}
override func didMove(to view: SKView) {
super.didMove(to: view)
// your stuff
}
}
class GenericScene: SKScene, LayoutSubviewDelegate {
func safeAreaUpdated(){}
override func didMove(to view: SKView) {
if let view = self.view, let controller = view.next, controller is GameViewController {
(controller as! GameViewController).layoutSubviewDelegate = self
}
}
}
References:
You can get margins through your view's safeAreaInsets. The insets will be zero on most devices, but non-zero on the iPhone X.
For example replace your line with this:
let safeAreaInsets = yourSpriteKitView.safeAreaInsets;
buyButton.position = CGPoint(x:safeAreaInsets.left - frame.maxX + frame.midX*2, y: safeAreaInsets.top - frame.maxY + frame.midY*1.655)

MKMapView covering SKSpriteNodes

I am experimenting with using MapKit and SpriteKit together, but I have hit a pretty major wall.
I have my map set up in my GameScene because I don't want the scene covering it, however when I attempt to add a SpriteNode to the view, it doesn't show. Upon reducing the map's alpha, I can see that the sprites are being rendered underneath the map.
Changing zPosition hasn't helped, as I'm assuming views such as MKMapViews are on a separate layer from SpriteKit objects.
Essentially what I am trying to accomplish is putting SpriteKit elements over top of my MKMapView (if possible, without storyboards - I am trying to do everything programmatically). I have spent hours trying to find an answer on the Internet but was unsuccessful.
Here is my GameScene:
import UIKit
import SpriteKit
import GameplayKit
import MapKit
import CoreLocation
class GameScene: SKScene, MKMapViewDelegate, CLLocationManagerDelegate {
let locationManager = CLLocationManager()
var map = MKMapView()
let healthBar = SKSpriteNode(imageNamed: "Square")
let maxHealthBar = SKSpriteNode(imageNamed: "Square")
var UID = String()
var ref = DatabaseReference()
static var username = String()
static var health = Int()
static var maxHealth = Int()
var width = CGFloat()
var right = CGFloat()
var left = CGFloat()
var top = CGFloat()
var bot = CGFloat()
var margin = CGFloat()
var middleX = CGFloat()
var middleY = CGFloat()
var maxBarWidth = CGFloat()
override func didMove(to view: SKView) {
map.frame = CGRect(x: 0, y: 0, width: self.size.width, height: self.size.height)
map.delegate = self
map.showsScale = false
map.showsPointsOfInterest = false
map.showsUserLocation = true
map.showsBuildings = true
map.isZoomEnabled = false
map.isScrollEnabled = false
map.isPitchEnabled = false
map.isRotateEnabled = false
let region = MKCoordinateRegionMakeWithDistance((locationManager.location?.coordinate)!, 400, 400)
map.setRegion(region, animated: false)
print("region:", map.region)
locationManager.requestWhenInUseAuthorization()
if (CLLocationManager.locationServicesEnabled()) {
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.startUpdatingLocation()
}
view.addSubview(map)
width = frame.width
right = frame.width
left = frame.width - frame.width
top = frame.height
bot = frame.height - frame.height
margin = frame.width / 75
middleX = frame.width / 2
middleY = frame.height / 2
maxBarWidth = width - margin * 2
let maxBarHeight = CGFloat(30)
let healthBarWidth = width - margin * 2
let barHeight = maxBarHeight * 0.75
maxHealthBar.anchorPoint = CGPoint(x: 0.0, y: 0.5)
maxHealthBar.position = CGPoint(x: middleX, y: top - margin - maxBarHeight / 2)
maxHealthBar.size = CGSize(width: maxBarWidth, height: maxBarHeight)
maxHealthBar.color = UIColor.black
maxHealthBar.colorBlendFactor = 1.0
maxHealthBar.zPosition = 1.1
self.addChild(maxHealthBar)
healthBar.anchorPoint = CGPoint(x: 0.0, y: 0.5)
healthBar.position = CGPoint(x: middleX - healthBarWidth / 2, y: maxHealthBar.position.y)
healthBar.size = CGSize(width: healthBarWidth, height: barHeight)
healthBar.color = UIColor.red
healthBar.colorBlendFactor = 1.0
healthBar.zPosition = 1000000
self.addChild(healthBar)
print("parent", healthBar.parent)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for t in touches {
let location = t.location(in: self)
takeDamage(damage: 10)
}
}
func takeDamage(damage: Int) {
GameScene.health -= damage
updateHealth()
}
func updateHealth() {
let percentFull = CGFloat(GameScene.health / GameScene.maxHealth)
let healthWidth = maxBarWidth * percentFull
healthBar.size.width = healthWidth
}
func mapView(_ mapView: MKMapView, didUpdate userLocation: MKUserLocation) {
map.setCenter((locationManager.location?.coordinate)!, animated: true)
}
override func update(_ currentTime: TimeInterval) {
// Called before each frame is rendered
}
}
And here is my GameViewController:
import UIKit
import SpriteKit
import GameplayKit
class GameViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
if (SignInScene.signInLoaded == false) {
if let scene = GameScene(fileNamed: "GameScene") {
// Configure the view.
let skView = self.view as! SKView
skView.showsFPS = true
skView.showsNodeCount = true
/* Sprite Kit applies additional optimizations to improve rendering performance */
skView.ignoresSiblingOrder = true
/* Set the scale mode to scale to fit the window */
scene.size = skView.bounds.size
scene.scaleMode = .aspectFill
skView.presentScene(scene)
}
}
}
override var shouldAutorotate: Bool {
return false
}
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
if UIDevice.current.userInterfaceIdiom == .phone {
return .portrait
} else {
return .portrait
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Release any cached data, images, etc that aren't in use.
}
override var prefersStatusBarHidden: Bool {
return false
}
}
I am extremely stuck on this one and would greatly appreciate any help!
Thanks in advance!
Your problem is your UIView heirarchy in your view controller,
Here is what your project looks like:
Controller
--SKView
----SKScene
--MKMapView
You need to change your hierarchy to be
Controller
--MKMapView
--SKView
----SKSceneView
I would recommend doing this inside your storyboard, so that you can keep design and code separated.
After you have changed your hierarchy, you need to do the following in your GameScene:
view.allowsTransparency = true
view.backgroundColor = .clear
backgroundColor = .clear
This will give you a view and scene that has no background color, which means that your map will show underneath it.
Now if you want touch events to hit the MKMapView and not the SKView, then you need to disable userInteractionEnabled on your MKMapView, or pass the touch events to the map from your SKView.

Why doesn't my snow fall to the ground?

I am trying to make a application (in swift) where snow falls in the background. The only problem is I have added the gravity animation, however the snow just stays where it is.
Here is my code:
ViewController.swift
import UIKit
#IBDesignable
class ViewController: UIViewController {
#IBInspectable var BgColor:UIColor = UIColor.whiteColor()
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = BgColor
/*listSubviewsOfView(self.view)*/ /*Not needed to answer this*/
var snow = Snow(frame: CGRect(x: 0, y: 0, width: 5, height: 5))
snow.opaque = false
self.view.addSubview(snow)
let animator = UIDynamicAnimator(referenceView: self.view)
let gravity = UIGravityBehavior(items: [snow])
let direction = CGVectorMake(0.0, 1.0)
gravity.gravityDirection = direction
animator.addBehavior(gravity)
/*
var snow = Snow(frame: CGRect(x: 0, y: 0, width: 5, height: 5))
snow.opaque = false
snow.viewHeight = UIScreen.mainScreen().bounds.height
snow.addSubview(snow)
let animator = UIDynamicAnimator(referenceView: view)
let gravity = UIGravityBehavior(items: [snow])
animator.addBehavior(gravity)
*/
}
/*Not needed to solve this*/
/*
func listSubviewsOfView(views: UIView) {
var index = 0
let randomNumbers = [Int](1...24).shuffle()
for view in views.subviews
{
if let _ = view.restorationIdentifier
{
view.setValue(String(Int(randomNumbers[index])), forKey: "updateText")
index++
}
if index == randomNumbers.count {
break
}
}
}
*/
}
Snow.swift:
import Foundation
import UIKit
#IBDesignable
class Snow:UIView
{
var viewHeight = CGFloat(0)
/*
required init(coder aDecoder: NSCoder) {
//Initilse UIView
super.init(coder: aDecoder)!
}
*/
override func drawRect(rect: CGRect) {
let path = UIBezierPath(ovalInRect: rect)
UIColor.whiteColor().setFill()
path.fill()
}
}
The question is, why does my snow stay on top of the screen at (0,0) and not fall down even though I have told it to have the gravity affect?
Make animator and gravity properties of your view controller.
import UIKit
#IBDesignable
class ViewController: UIViewController {
#IBInspectable var BgColor:UIColor = UIColor.whiteColor()
var animator: UIDynamicAnimator? = nil;
let gravity = UIGravityBehavior()
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = UIColor.blackColor()
let snow = Snow(frame: CGRect(x: 0, y: 0, width: 5, height: 5))
snow.opaque = false
self.view.addSubview(snow)
animator = UIDynamicAnimator(referenceView:self.view);
animator?.addBehavior(gravity)
gravity.addItem(snow)
let direction = CGVectorMake(0.0, 1.0)
gravity.gravityDirection = direction
}
}

Resources