add UITextField to sprite kit - uitextfield

hi I've been following some tutorials but this ones for swift 1.2
I'm on Xcode 7.2 think its swift 2.1
he uses a UITextfield with spriteKit
but this line causes an error
I've googled but can't find any help
so thought this is the place to help cheers
self.view!.addSubview(TextInput!)
the whole code is
import SpriteKit
class GameScene: SKScene
{ var TextInput:UITextField?
override func didMoveToView(view: SKView)
{
let myLabel = SKLabelNode(fontNamed:"Chalkduster")
myLabel.text = ""
myLabel.fontSize = 15
myLabel.position = CGPoint(x:CGRectGetMidX(self.frame), y:CGRectGetMidY(self.frame))
self.addChild(myLabel)
TextInput?.frame = CGRect(x: 200, y: 300, width: 100, height: 40)
self.view!.addSubview(TextInput!)
TextInput?.backgroundColor = UIColor.whiteColor()
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
/* Called when a touch begins */
for touch in touches
{
let location = touch.locationInNode(self)
}
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
} }

You don't seem to be initialising your UITextField anywhere.
Try adding this line to didMoveToView before you set the frame of TextInput:
self.TextInput = UITextField()
Just to note, variable names in Swift should be written in camel case (thisIsCamelCase), so it might be better to rename your variable as textInput. I can't actually find this in the Apple documentation right now, but this convention is used in all of Apple's example code, and is stated by various other sources, e.g.
https://github.com/raywenderlich/swift-style-guide
http://andybargh.com/variables-and-constants-in-swift/#Rules_for_Naming_Variables

Related

SpriteKit Controls / Gesture

Essentially I am trying to incorporate X2 gamescene buttons that do the following functions:
1) Tap to fly (this I have working)
2) Tap to shoot a projectile from Player position (I do not have working).
My problem is I currently have the fly func set when touched anywhere on the screen. I have tried the following :
This is in reference to my GameScene : I thought in order to split this out I would need a node on the screen to reference this function. This does not error in the console but does not appear in the GameScene.
// Button to trigger shooting :
let btnTest = SKSpriteNode(imageNamed: "Crater")
btnTest.setScale(0.2)
btnTest.name = "Button"
btnTest.zPosition = 10
btnTest.position = CGPoint(x: 100, y: 200)
self.addChild(btnTest)
Next in the Player class I have the following broken down:
var shooting = false
var shootAnimation = SKAction()
var noshootAnimation = SKAction()
Init func:
self.run(noshootAnimation, withKey: "noshootAnimation")
let projectile = SKSpriteNode(imageNamed: "Crater")
projectile.position = CGPoint (x: 100, y: 50)
projectile.zPosition = 20
projectile.name = "projectile"
// Assigning categories to Game objects:
self.physicsBody?.categoryBitMask =
PhysicsCategory.plane.rawValue
self.physicsBody?.contactTestBitMask =
PhysicsCategory.ground.rawValue
self.physicsBody?.collisionBitMask =
PhysicsCategory.ground.rawValue
self.physicsBody?.applyImpulse(CGVector(dx: 300, dy: 0))
self.addChild(projectile)
// Start the shoot animation, set shooting to true:
func startShooting() {
self.removeAction(forKey: "noshootAnimation")
self.shooting = true
}
// Stop the shoot animation, set shooting to false:
func stopShooting() {
self.removeAction(forKey: "shootAnimation")
self.shooting = false
}
The node appears in the GameScene which looks promising, finally I move to the last bit of code in the GameScene as follows:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) for touch in (touches) {
let location = touch.location(in: self)
let nodeTouched = atPoint(location)
if let gameSprite = nodeTouched as? GameSprite {
gameSprite.onTap()
}
// Check the HUD buttons which I have appearing when game is over…
if nodeTouched.name == "restartGame" {
// Transition to a new version of the GameScene
// To restart the Game
self.view?.presentScene(GameScene(size: self.size), transition: .crossFade(withDuration: 0.6))
}
else if nodeTouched.name == "returnToMenu"{
// Transition to the main menu scene
self.view?.presentScene(MenuScene(size: self.size), transition: . crossFade(withDuration: 0.6))
}
}
Player.startFly()
player.startShooting()
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
Player.stopFly()
player.stopShooting()
}
override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
Player.stopFly()
player.stopShooting()
}
override func update(_ currentTime: TimeInterval) {
player.update()
}
}
Unfortunately nothing happens in the GameScene and the node doesn’t fire when the screen is pressed, with the code above is there anyway I can amend this to allow for both ‘tap to fly’ + ‘tap to shoot’ functions. I still can’t figure out how to get the button I had declared early on in the GameScene to appear in which my gesture / touch position can be localised to this node on the screen as oppose to the whole screen in which I have currently..
I can say this sounded more simple in my head to begin with than actually coding together.
Firstly, the above code does not have any calls to run the animation with the key "shootAnimation" and is missing the call to re-run the not-shooting animation when stopShooting() and the call to re-run the shooting animation in startShooting(). The methods should include these as shown below
func startShooting() {
self.removeAction(forKey: "noshootAnimation")
// Add this call
self.run(shootAnimation)
self.shooting = true
}
func stopShooting() {
self.removeAction(forKey: "shootAnimation")
// Add this call
self.run(noshootAnimation)
self.shooting = true
}
Secondly, the noshootAnimation and shootAnimation animations are empty actions: they will not do anything if initialized as SKAction(). Depending on what you are trying to do, there are a number of class methods of SKAction that will create actions for you here.
Thirdly, any SKNode (recall that a scene is an SKNode subclass) will not receive touch calls if the node's isUserInteractionEnabled property is set to false (to which it is defaulted); instead, it will be treated as if its parent received the call. However, should a node's isUserInteractionEnabled be true, it will be the one that receives the UIResponder calls, not its parent node. Make sure that this property is set to true for the scene and for any nodes that need to receive touches (you can do this in didMove(to:) in the scene or elsewhere).
I will now propose an improvement. I frequently use buttons in Spritekit, but they are all subclasses of a custom button class (itself a subclass of SKSpriteNode) that uses protocol delegation to alert members of touch events. The scheme looks like this:
class Button: SKSpriteNode {
weak var responder: ButtonResponder?
// Custom code
// MARK: UIResponder
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesBegan(touches, with: event)
// Respond here
responder?.respond(to: self)
}
}
protocol ButtonResponder: AnyObject {
func respond(to button: Button)
}
class MyScene: SKScene, ButtonResponder {
func respond(to button: Button) {
// Do something on touch, but typically check the name
switch button.name {
case "myButton":
// Do something specific
default:
break
}
}
override func didMove(to view: SKView) {
// Set the scene as a responder
let buttonInScene = childNode(withName: "myButton") as! Button
buttonInScene.responder = self
}
}
For more on delegation, look here. Hopefully this helped a little bit.
EDIT: Convert touch location
You can convert the touch to the node on the screen by passing a reference to the location(in:) method of UITouch instead of the scene as you did in your code
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesBegan(touches, with: event)
let yourNode = childNode(withName: "yourNode")!
for touch in touches {
let touchLocationToYourNode = touch.location(in: yourNode)
// Do something
}
}
Alternatively, use SKNode's convert(_:from:) and convert(_:to:) methods
let nodeA = SKNode()
let nodeB = SKNode()
nodeA.xScale = 1.5
nodeA.yScale = 1.2
nodeA.position = .init(x: 100, y: 100)
nodeA.zRotation = .pi
let pointInNodeACoordinateSystem = CGPoint(x: 100, y: 100)
let thatSamePointInNodeBCoordinateSystem = nodeB.convert(pointInNodeACoordinateSystem, from: nodeA)

Why doesn't Metal render my simple clear window code?

I have been following this tutorial. I downloaded the source and tried "translating" it to Swift. This is my "translated" code:
import Cocoa
import AppKit
import MetalKit
import simd
class MetalViewController: NSViewController {
#IBOutlet var inview: MTKView!
override func viewDidLoad() {
super.viewDidLoad()
let _view: MTKView = self.inview
_view.device = MTLCreateSystemDefaultDevice()
let _renderer: Renderer=initView(view: _view)
_view.delegate=_renderer as? MTKViewDelegate
_view.preferredFramesPerSecond=60
}
}
class Renderer: NSObject {
init(device: MTLDevice){
self._device=device
self._commandQueue=_device.makeCommandQueue()!
super.init()
}
func mtkView(_ view: MTKView, drawableSizeWillChange size: CGSize) {
}
func draw(in view: MTKView) {
let color = Color(red: 1.0,green: 0.0,blue: 0.0,alpha: 0.0)
view.clearColor = MTLClearColorMake(color.red, color.green, color.blue, color.alpha)
let commandbuffer = _commandQueue.makeCommandBuffer()
let renderpassdescriptor: MTLRenderPassDescriptor = view.currentRenderPassDescriptor!
let renderencoder: MTLRenderCommandEncoder = (commandbuffer?.makeRenderCommandEncoder(descriptor: renderpassdescriptor))!
renderencoder.endEncoding()
commandbuffer!.present(view.currentDrawable!)
commandbuffer!.commit()
}
var _device: MTLDevice
var _commandQueue: MTLCommandQueue
}
struct Color{
var red, green, blue, alpha: Double
}
func initView(view: MTKView) -> Renderer{
var renderer: Renderer
renderer=Renderer(device: view.device!)
return renderer
}
So I put the AAPLRenderer and AAPLViewControllers into one file, and made it so that there are no header files. I linked the view with #IBOutlet to the view controller because the view was a NSView and I cannot cast it to MTKView without getting a compile time error. The AppDelegate is the original one and I do not have a main file.
I end up with a window that does not show red, but rather shows nothing. I do not understand why this is happening. Please help me, thank you.
I see two issues.
1) MTKView's delegate property is a weak var, which means that if you don't hold onto an instance of your renderer, it'll be immediately deinited and never receive any delegate callbacks. Keep a reference to your renderer as a property on your view controller.
class MetalViewController: NSViewController {
#IBOutlet var inview: MTKView!
var renderer: Renderer!
override func viewDidLoad() {
// ...
let view: MTKView = self.inview
// ...
renderer = initView(view: view)
view.delegate = renderer
// ...
}
}
2) Because the Renderer class doesn't explicitly declare conformance to the MTKViewDelegate protocol, the conditional cast when assigning it as the view's delegate fails. Make Renderer explicitly conform to the protocol, and remove the conditional cast as shown above.
class Renderer: NSObject, MTKViewDelegate
Well, it could be anything. But, the first thing I would check is that your alpha setting for that red color should have alpha = 1.0 and not alpha = 0.0.

Reading right mouse clicks number of clicks

I have been experimenting with mouse clicks. I am ok with left mouse clicks getting raw values etc etc. I now want to add right mouse clicks. I have setup a basic example. What i would like to achieve if there is one mouse right click it performs one function, and if there is two mouse right clicks it performs a different function. The problem is if you do two mouse clicks it obviously cannot differentiate between the two and so fire the one mouse click function before performing the second mouse function.
I was thinking of maybe using a timer of some sort to record the number of click. But i end up going round in circles as i just seem to start the timers over and over. I'm hoping some one might help. thanks for reading here is the code.
Xcode 8 Swift 3 Mac OSX Sierra NOT IOS.. NOT IOS
import Cocoa
class MainWindowController: NSWindowController {
#IBOutlet weak var MyView: NSView!
override func windowDidLoad() {
super.windowDidLoad()
//Initialize mouse for Right Click numberOfClicksRequired = 1
let recogRightClick1 = NSClickGestureRecognizer(target: self, action: #selector(oneMouseClick))
recogRightClick1.buttonMask = 0x2
recogRightClick1.numberOfClicksRequired = 1
MyView.addGestureRecognizer(recogRightClick1)
//Initialize mouse for Right ClicknumberOfClicksRequired = 2
let recogRightClick2 = NSClickGestureRecognizer(target: self, action: #selector(twoMouseClick(myrec:myRightClick:)))
recogRightClick2.buttonMask = 0x2
recogRightClick2.numberOfClicksRequired = 2
MyView.addGestureRecognizer(recogRightClick2)
}//EO Overide
func oneMouseClick(myrec: NSPanGestureRecognizer,myRightClick:NSClickGestureRecognizer){
let rightClick = myRightClick.state.rawValue
print("oneMouseClick",rightClick)
}
func twoMouseClick(myrec: NSPanGestureRecognizer,myRightClick:NSClickGestureRecognizer){
let rightClick = myRightClick.state.rawValue
print("twoMouseClick",rightClick)
}
}//EnD oF thE wORld
UPDATE
I have re generated the code following the advice given. Now the code reflects more of what i wanted to do. My only problem is that I would like all the mouse operations to be triggered only inside 'myView' rather than within the main window. I thought it might have something to do with first responder but that doesn't seem to work. Again any thought would be appreciated. Please excuse any bad code i'm self taught.
Xcode 8 Swift 3 Mac OSX Sierra NOT IOS.. NOT IOS
import Cocoa
class MainWindowController: NSWindowController {
#IBOutlet weak var myView: NSView!
var mouseX:CGFloat = 0
var mouseY:CGFloat = 0
override func windowDidLoad() {
myView.window?.becomeFirstResponder()
myView.window?.acceptsMouseMovedEvents = true
super.windowDidLoad()
myView.wantsLayer = true
myView.layer?.backgroundColor = CGColor(red: 0.05, green: 0.57, blue: 0.80, alpha: 0.6)
NSEvent.addLocalMonitorForEvents(matching:.leftMouseDown){
self.mouseEventFunction(data: 1)
return $0
}
NSEvent.addLocalMonitorForEvents(matching:.leftMouseUp){
self.mouseEventFunction(data:2)
return $0
}
NSEvent.addLocalMonitorForEvents(matching:.rightMouseDown){
self.mouseEventFunction(data: 3)
return $0
}
NSEvent.addLocalMonitorForEvents(matching:.rightMouseUp){
self.mouseEventFunction(data: 4)
return $0
}
NSEvent.addLocalMonitorForEvents(matching:.mouseMoved) {
self.mouseX = NSEvent.mouseLocation().x
self.mouseY = NSEvent.mouseLocation().y
return $0 }
}//EO Overide
func mouseEventFunction (data: Int){
switch data{
case 1 :
print("LeftMouseDown")
case 2 :
print("LeftMouseUp")
case 3 :
print("RightMouseDown")
case 3 :
print("RightMouseUP")
default: break
}
if data == 1 {print("mouseX",mouseX,"mouseY",mouseY)}
}//eo mouseEvent
}//EnD oF thE wORld
UPDATE 2
I have now updated subClassing the view controller, so the mouse clicks are now only working in myView. I'm still having problems with 'func mouseDragged' What i need to achieve is the bottom left of my view is x = 0 and Y = 0. I had a try with converting but thats not working. hoping someone might guide me. thanks for reading here is the updated code.
Xcode 8 Swift 3 Mac OSX Sierra NOT IOS.. NOT IOS
import Cocoa
class MainWindowController: NSWindowController {
#IBOutlet weak var myView: NSView!
override func windowDidLoad() {
myView.window?.becomeFirstResponder()
myView.window?.acceptsMouseMovedEvents = true
window?.contentView?.addSubview(myView)
super.windowDidLoad()
}//EO Overide
}//EnD oF thE wORld
class testView: NSView {
override func draw(_ dirtyRect: NSRect) {
let backgroundColor = NSColor.lightGray
backgroundColor.set()
NSBezierPath.fill(bounds)
}
override func mouseDragged(with theEvent: NSEvent) {
let myLocationInWindow = theEvent.locationInWindow
let location = convert(myLocationInWindow, to: self)
Swift.print("myLocationInWindow",myLocationInWindow,"location",location)
}
override func mouseDown(with theEvent: NSEvent) {
Swift.print("mouseDown")
}
override func mouseUp(with theEvent: NSEvent) {
Swift.print("mouseUp clickCount: \(theEvent.clickCount)")
}
}//eo testView
To define mouse inside view you use
let myLocationInWindow = theEvent.locationInWindow
let location = convert(myLocationInWindow, from: nil)
where nil is the window
here is the final code
import Cocoa
class MainWindowController: NSWindowController {
#IBOutlet weak var myView: NSView!
override func windowDidLoad() {
super.windowDidLoad()
}//EO Overide
}//EnD oF thE wORld
class testView: NSView {
override func draw(_ dirtyRect: NSRect) {
let backgroundColor = NSColor.lightGray
backgroundColor.set()
NSBezierPath.fill(bounds)
}
override func mouseDragged(with theEvent: NSEvent) {
let myLocationInWindow = theEvent.locationInWindow
let location = convert(myLocationInWindow, from: nil)
Swift.print("location",location)
}
override func mouseDown(with theEvent: NSEvent) {
Swift.print("mouseDown")
}
override func mouseUp(with theEvent: NSEvent) {
Swift.print("mouseUp clickCount: \(theEvent.clickCount)")
}
}//eo testView

Can't compile swift program from a 1 year old tutorial

Very low level question but;
Why wont this compile for me? I copied it down exactly from a tutorial and it worked before I updated my xcode to a newer version to do things later in the tutorial but I guess it updated too far ahead. Thanks for any help you can offer.
import SpriteKit
class GameScene: SKScene {
let Mainball = SKSpriteNode(imageNamed: "Ball")
override func didMoveToView(view: SKView) {
/* Setup your scene here */
Mainball.size = CGSize(width: 100, height: 100)
Mainball.position = CGPoint(x: scene!.frame.width / 2, y: scene!.frame.height / 2)
self.addChild(Mainball)
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
/* Called when a touch begins */
for touch in touches {
let location = touch.locationInNode(self)
}
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
}
}

iAd in xcode 6 with Swift

I'm working to implement a banner ad in the scene, but it always reports "Thread 1: EXC_BREAKPOINT(code=EXC_ARM_BREAKPOINT, subcode=Oxdefe) and the program stops running.
I referenced Mr. T's answer in another question about iAd("Swift - ADBannerView") but still couldn't make it.
The code looks like this:
import UIKit
import SpriteKit
import iAd
class GameViewController: UIViewController, ADBannerViewDelegate {
#IBOutlet var adBannerView: ADBannerView
override func viewDidLoad() {
super.viewDidLoad()
println("view loaded")
//iAd
self.canDisplayBannerAds = true
self.adBannerView.delegate = self
self.adBannerView.alpha = 0.0
if let scene = GameScene.unarchiveFromFile("GameScene") as? 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.scaleMode = .AspectFill
skView.presentScene(scene)
}
}
//iAd
func bannerViewWillLoadAd(banner: ADBannerView!) {
println("sort of working1")
}
func bannerViewDidLoadAd(banner: ADBannerView!) {
self.adBannerView.alpha = 1.0
println("sort of working2")
}
func bannerViewActionDidFinish(banner: ADBannerView!) {
println("sort of working3")
}
func bannerViewActionShouldBegin(banner: ADBannerView!, willLeaveApplication willLeave: Bool) -> Bool {
println("sort of working4")
return true
}
func bannerView(banner: ADBannerView!, didFailToReceiveAdWithError error: NSError!) {
}
}
And I created an ADBannerView in the Main.storyboard and linked it with the #IBOutlet adBannerView.
Anyone helps me figure out?
This is how I did it, possibly not all of it is necessary.
I did not use the banner in the Storyboard, so the IBOutlet is not necessary.
Also, if you manually create a banner, you do not need to set self.canDisplayBannerAds
This function (ported from ObjC) is how I display the ads.
func loadAds(){
adBannerView = ADBannerView(frame: CGRect.zeroRect)
adBannerView.center = CGPoint(x: adBannerView.center.x, y: view.bounds.size.height - adBannerView.frame.size.height / 2)
adBannerView.delegate = self
adBannerView.hidden = true
view.addSubview(adBannerView)
}
This is called in viewDidLoad. Then, in the didLoadAd delegate method, I set adBannerView.hidden = false and in didFailToReceiveAdWithError, adBannerView.hidden = true
I think hidden is better than alpha in this situation, as it feels more natural. I believe (but am not sure) that when hidden, the view is not drawn at all by the GPU, while with an alpha of 0, it is still drawn, but made invisible (correct me if I am wrong).
This is my setup, and it worked for me, so hopefully it will work in your case too!

Resources