I just started learning Swift and I have been consulting Apple's Dev Guide but some of the syntax when dealing with properties and pointers is throwing me off. Apple explains that properties just use dot syntax and even the API seems to reflect that but it's not working to my desired to effect. Here's what I've been working on specifically. I am also struggling with instance initialization. I have already properly created a bridging header Any guidance would be appreciated. Thanks!
This is the code I am trying to emulate in swift.
#property (nonatomic) SFWaterNode *waterSurface;
#property (nonatomic) NSTimeInterval lastUpdateTime;
#end
#implementation GameScene
- (void)didMoveToView:(SKView *)view {
self.backgroundColor = [SKColor blackColor];
CGPoint startPoint = CGPointMake(-1, self.size.height/2);
CGPoint endPoint = CGPointMake(self.size.width, self.size.height/2);
self.waterSurface = [SFWaterNode nodeWithStartPoint:startPoint endPoint:endPoint depth:self.size.height/2 color:[SKColor blueColor]];
[self addChild:self.waterSurface];
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInNode:self];
[self.waterSurface splash:location speed:-50];
}
- (void)update:(CFTimeInterval)currentTime {
[self.waterSurface update:currentTime];
}
#end
Try this code below:
public var waterSurface:SFWaterNode?
var lastUpdateTime:NSTimeInterval
func didMoveToView(view:SKView) {
self.backgroundColor = .black
let startPoint:CGPoint = CGPoint.init(x: -1, y: self.size.height/2)
let endPoint:CGPoint = CGPoint(x: -1, y: self.size.height/2)
self.waterSurface = SFWaterNode.nodeWithStartPoint(startPoint, endPoint:endPoint, depth:self.size.height/2, color:.blue)
self.addChild(self.waterSurface)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch:UITouch = touches.anyObject()
let location:CGPoint = touch.location(inNode: self)
self.waterSurface.splash(location, speed:-50)
}
func update(curentTime:CFTimeInterval) {
self.waterSurface.update(currentTime)
}
Related
I have a circle called circle and a line called ground in the View.
I can drag & move the circle in the whole view, but when I try to limit it (in the code) to move only under the ground area - it works (when I try to drag it above the ground) and the circle moves only by my x location dragging, BUT when I try to drag it back under the ground - the circle only moves by the x location dragging and the y location dragging doesn't change (the y is the ground y).
How can I make it move back again, in a free way, under the ground and not only by the x location of the dragging?
Here is what I tried so far:
let ground = SKSpriteNode()
var circle = SKShapeNode(circleOfRadius: 40)
override func didMoveToView(view: SKView) {
/* Setup your scene here */
// ground.physicsBody?.dynamic = false
ground.color = SKColor.redColor()
ground.size = CGSizeMake(self.frame.size.width, 5)
ground.position = CGPoint(x: CGRectGetMidX(self.frame), y: self.frame.size.height / 5)
self.addChild(ground)
circle.position = CGPointMake(CGRectGetMidX(self.frame) ,CGRectGetMinY(ground.frame) - (circle.frame.size.height / 2))
circle.strokeColor = SKColor.blackColor()
circle.glowWidth = 1.0
circle.fillColor = SKColor.orangeColor()
self.addChild(circle)
}
var lastTouch: CGPoint? = nil
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
/* Called when a touch begins */
let touch = touches.first
let touchLocation = touch!.locationInNode(self)
if circle.position.y < ground.position.y - (circle.frame.size.height / 2){
circle.position = touchLocation
} else {
circle.position.x = CGFloat(touchLocation.x)
}
}
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
let touch = touches.first
let touchLocation = touch!.locationInNode(self)
if circle.position.y < ground.position.y - (circle.frame.size.height / 2) {
circle.position = touchLocation
} else {
circle.position.x = CGFloat(touchLocation.x)
}
}
I have solved it:
In touchesMoved func I wrote this code:
for touch in touches {
if touchLocation.y < ground.position.y {
circle.position = touchLocation
} else {
circle.position.x = touchLocation.x
}
}
I have this object that is a SKSpriteNode created from an image. The whole scene area itself is the boundary from where this object should not escape. Top and bottom boundaries are recognised but not the sides? Any help appreciated. Thanks
import SpriteKit
var ship = SKSpriteNode()
class GameScene: SKScene {
override func didMoveToView(view: SKView) {
self.backgroundColor = SKColor.blackColor()
self.physicsBody = SKPhysicsBody(edgeLoopFromRect: self.frame)
self.physicsWorld.gravity = CGVectorMake(0.0, -9.8)
ship = SKSpriteNode(imageNamed: "spark")
ship.position = CGPointMake(200, 400)
ship.physicsBody.linearDamping = 0
ship.physicsBody.restitution = 0.95
ship.physicsBody.friction = 0
self.addChild(ship)
var myVector = CGVectorMake(20, 20)
ship.physicsBody.applyImpulse(myVector)
let emitter = SKEmitterNode(fileNamed: "myExhaust")
emitter.position = CGPointMake(0.0, -ship.size.height / 2.0)
ship.addChild(emitter)
emitter.targetNode = self
}
override func touchesMoved(touches: NSSet, withEvent event: UIEvent) {
/* Called when a touch begins */
var touch = touches.anyObject() as UITouch
// ship.position = [touch locationInNode:self];
ship.position = touch.locationInNode(self)
}
}
Try using the SKScene's size to create a CGRect and don't use SKScene.frame directly. SKScene.size determines the size of the visible portion of the SKScene and may be different from SKScene.frame.size.
override func didMoveToView(view: SKView) {
physicsBody = SKPhysicsBody(edgeLoopFromRect: CGRectMake(0, 0, size.width, size.height))
// ...
}
(Side Note: with Swift you don't need to use self there)
I have SKSpriteNode which is forced to fall to the ground because of self.physicsWorld.gravity = CGVectorMake(0.0, -4.0);. When user taps on display and holds it, I'd like the SKSpriteNode to fly up, and after user stops holding touch, it again falls down to the ground.
I have tried to change velocity, but it only makes small bounce, like applyImpulse method...:
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
/* Called when a touch begins */
for touch: AnyObject in touches {
let location = touch.locationInNode(self)
flyingObject.physicsBody.velocity = CGVectorMake(0, 100)
}
}
edit:
here's my scene initialization code
override func didMoveToView(view: SKView) {
/* Setup your scene here */
// Physics
self.physicsWorld.gravity = CGVectorMake(0.0, -4.0);
// flyingObject
var flyingObjectTexture = SKTexture(imageNamed:"flyingObject")
flyingObject.filteringMode = SKTextureFilteringMode.Nearest
flyingObject = SKSpriteNode(texture: flyingObjectTexture)
flyingObject.setScale(1)
flyingObject.position = CGPoint(x: self.frame.size.width * 0.35, y: self.frame.size.height * 0.6)
flyingObject.physicsBody = SKPhysicsBody(circleOfRadius:flyingObject.size.height/2.0)
flyingObject.physicsBody.dynamic = true
flyingObject.physicsBody.allowsRotation = false
self.addChild(flyingObject)
// Ground
var groundTexture = SKTexture(imageNamed:"Ground")
var sprite = SKSpriteNode(texture:groundTexture)
sprite.setScale(0.5)
sprite.position = CGPointMake(self.size.width/2 - 100, sprite.size.height)
self.addChild(sprite)
var ground = SKNode()
ground.position = CGPointMake(0, groundTexture.size().height / 2)
ground.physicsBody = SKPhysicsBody(rectangleOfSize: CGSizeMake(self.frame.width, groundTexture.size().height * 0.5))
ground.physicsBody.dynamic = false
self.addChild(ground)
}
thanks to Code Monkey I was able to come up with solution, it's easier than I thought:
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
/* Called when a touch begins */
isTouching = true
}
override func touchesEnded(touches: NSSet!, withEvent event: UIEvent!) {
/* Called when a touch begins */
isTouching = false
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
if isTouching {
flyingObject.physicsBody.applyImpulse(CGVectorMake(0, 5))
}
}
touches began:
- (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch * touch = [touches anyObject];
CGPoint location = [touch locationInNode:self];
flyingObject.physicsBody.velocity = CGVectorMake(0, 100)
}
touches ended:
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch * touch = [touches anyObject];
CGPoint location = [touch locationInNode:self];
flyingObject.physicsBody.velocity = CGVectorMake(0, 0)
}
I want to create sparkle effect over an image as shown in the video Sparkle Effect
the only way I could think of doing it animating each particle separately using core animation, but that would be inefficient as well as time consuming.
Is there any other way I can do the same?
Here is a solution from Erica Susan's cook book. See this works for you.
You can add visual interest to your interfaces by using emitters in tandem with user touches. The following class demonstrates how to follow a touch over its lifetime, adding a little sparkle to wherever the user touches on-screen.
The class begins as soon as the user touches the screen, creating an emitter layer and a single emitter cell. The cell defines the particles — their color, their birth rate, their lifetime, velocity, and so forth.
As the user's touch progresses, this class updates the emitter's location, removing the emitter once the touch is removed from the screen. Although this example is written for single touch interaction, you can easily update the code to add an array of emitters (rather than a single instance) for multi-touch interaction.
Emitters are easily added to your projects and efficient to run. While too much animation is never a good design idea, a little sparkle used judiciously can add life and movement.
#interface SparkleTouchView : UIView {
CAEmitterLayer *emitter;
}
#end
#implementation SparkleTouchView
- (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
float multiplier = 0.25f;
CGPoint pt = [[touches anyObject] locationInView:self];
//Create the emitter layer
emitter = [CAEmitterLayer layer];
emitter.emitterPosition = pt;
emitter.emitterMode = kCAEmitterLayerOutline;
emitter.emitterShape = kCAEmitterLayerCircle;
emitter.renderMode = kCAEmitterLayerAdditive;
emitter.emitterSize = CGSizeMake(100 * multiplier, 0);
//Create the emitter cell
CAEmitterCell* particle = [CAEmitterCell emitterCell];
particle.emissionLongitude = M_PI;
particle.birthRate = multiplier * 1000.0;
particle.lifetime = multiplier;
particle.lifetimeRange = multiplier * 0.35;
particle.velocity = 180;
particle.velocityRange = 130;
particle.emissionRange = 1.1;
particle.scaleSpeed = 1.0; // was 0.3
particle.color = [[COOKBOOK_PURPLE_COLOR colorWithAlphaComponent:0.5f] CGColor];
particle.contents = (__bridge id)([UIImage imageNamed:#"spark.png"].CGImage);
particle.name = #"particle";
emitter.emitterCells = [NSArray arrayWithObject:particle];
[self.layer addSublayer:emitter];
}
- (void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
CGPoint pt = [[touches anyObject] locationInView:self];
// Disable implicit animations
[CATransaction begin];
[CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions];
emitter.emitterPosition = pt;
[CATransaction commit];
}
- (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
[emitter removeFromSuperlayer];
emitter = nil;
}
- (void) touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
[self touchesEnded:touches withEvent:event];
}
#end
Don't forget to create a png file named spark.png in order to create the animation.
/*Tested in swift 5.1*/
Import UIKit
enum Images {
static let box = UIImage(named: "Box")!
static let triangle = UIImage(named: "Triangle")!
static let circle = UIImage(named: "Circle")!
static let swirl = UIImage(named: "Spiral")!
}
class ActivationRequiredViewController: UIViewController {
var emitter = CAEmitterLayer()
var colors:[UIColor] = [
Colors.red,
Colors.blue,
Colors.green,
Colors.yellow
]
var images:[UIImage] = [
Images.box,
Images.triangle,
Images.circle,
Images.swirl
]
var velocities:[Int] = [
100,
90,
150,
200
]
override func viewDidLoad() {
super.viewDidLoad()
startSparkle()
}
func startSparkle() {
emitter.emitterPosition = CGPoint(x: self.view.frame.size.width / 2, y: -10)
emitter.emitterShape = CAEmitterLayerEmitterShape.line
emitter.emitterSize = CGSize(width: self.view.frame.size.width, height: 2.0)
emitter.emitterCells = generateEmitterCells()
self.view.layer.addSublayer(emitter)
}
private func generateEmitterCells() -> [CAEmitterCell] {
var cells:[CAEmitterCell] = [CAEmitterCell]()
for index in 0..<16 {
let cell = CAEmitterCell()
cell.birthRate = 4.0
cell.lifetime = 14.0
cell.lifetimeRange = 0
cell.velocity = CGFloat(getRandomVelocity())
cell.velocityRange = 0
cell.emissionLongitude = CGFloat(Double.pi)
cell.emissionRange = 0.5
cell.spin = 3.5
cell.spinRange = 0
cell.color = getNextColor(i: index)
cell.contents = getNextImage(i: index)
cell.scaleRange = 0.25
cell.scale = 0.1
cells.append(cell)
}
return cells
}
private func getRandomVelocity() -> Int {
return velocities[getRandomNumber()]
}
private func getRandomNumber() -> Int {
return Int(arc4random_uniform(4))
}
private func getNextColor(i:Int) -> CGColor {
if i <= 4 {
return colors[0].cgColor
} else if i <= 8 {
return colors[1].cgColor
} else if i <= 12 {
return colors[2].cgColor
} else {
return colors[3].cgColor
}
}
private func getNextImage(i:Int) -> CGImage {
return images[i % 4].cgImage!
}
}
For anyone doing a Mac app, here's the same concept as #thevikasnayak , for an NSViewController
import Cocoa
enum Images {
static let box = NSImage(named: "Box")!
static let triangle = NSImage(named: "Triangle")!
static let circle = NSImage(named: "Circle")!
static let swirl = NSImage(named: "Spiral")!
}
class SparkleVC: NSViewController {
var emitter = CAEmitterLayer()
var colors:[NSColor] = [
NSColor.blue.withAlphaComponent(0.1),
NSColor.blue.withAlphaComponent(0.2),
NSColor.blue.withAlphaComponent(0.3),
NSColor.blue.withAlphaComponent(0.4)]
var images:[NSImage] = [Images.box, Images.triangle, Images.circle, Images.swirl]
override func viewDidAppear() {
super.viewDidAppear()
view.wantsLayer = true
startSparkle()
}
func startSparkle() {
emitter.emitterPosition = CGPoint(x: view.bounds.size.width / 2.0, y: view.bounds.size.height / 2.0)
emitter.emitterShape = CAEmitterLayerEmitterShape.circle
emitter.emitterSize = CGSize(width: view.bounds.size.width / 4.0, height: view.bounds.size.width / 4.0)
emitter.emitterCells = generateEmitterCells()
view.layer!.addSublayer(emitter)
}
private func generateEmitterCells() -> [CAEmitterCell] {
var cells:[CAEmitterCell] = [CAEmitterCell]()
for index in 0..<16 {
let cell = CAEmitterCell()
cell.birthRate = 200.0
cell.lifetime = 0.01
cell.lifetimeRange = 0.03
cell.velocity = 5
cell.velocityRange = 2
cell.emissionLatitude = Double.random(in: 0 ..< Double.pi)
cell.emissionLongitude = Double.random(in: 0 ..< Double.pi)
cell.emissionRange = Double.pi
cell.spin = 10
cell.spinRange = 10
cell.color = colors[index/4].cgColor
cell.contents = nextImage(i: index)
cell.scaleRange = 0.2
cell.scale = 1
cells.append(cell)
}
return cells
}
private func nextImage(i:Int) -> CGImage {
return (images[i % 4]).cgImage(forProposedRect: nil, context: nil, hints: nil)!
}
}
Please check this solution.
https://github.com/GabriellaQiong/CIS581-Project2-Image-Morphing
CIwarpKernel Kets you warp an image according to reference points. That is exactly what you want to do in order to have image morphing.
I need to drag my UIView object. I use this code, but it does not work
float oldX, oldY;
BOOL dragging;
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [[event allTouches] anyObject];
CGPoint touchLocation = [touch locationInView:self];
if ([[touch.view class] isSubclassOfClass:[UILabel class]]) {
UILabel *label = (UILabel *)touch.view;
if (CGRectContainsPoint(label.frame, touchLocation)) {
dragging = YES;
oldX = touchLocation.x;
oldY = touchLocation.y;
}
}
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
dragging = NO;
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [[event allTouches] anyObject];
CGPoint touchLocation = [touch locationInView:self];
if ([[touch.view class] isSubclassOfClass:[UILabel class]]) {
UILabel *label = (UILabel *)touch.view;
if (dragging) {
CGRect frame = label.frame;
frame.origin.x = label.frame.origin.x + touchLocation.x - oldX;
frame.origin.y = label.frame.origin.y + touchLocation.y - oldY;
label.frame = frame;
}
}
}
I would suggest using a UIPanGestureRecognizer:
-(void)dragging:(UIPanGestureRecognizer *)gesture
{
// Check if this is the first touch
if(gesture.state == UIGestureRecognizerStateBegan)
{
// Store the initial touch so when we change positions we do not snap
self.panCoord = [gesture locationInView:gesture.view];
[self.view bringSubviewToFront:gesture.view];
}
CGPoint newCoord = [gesture locationInView:gesture.view];
// Create the frame offsets to use our finger position in the view.
float dX = newCoord.x-self.panCoord.x;
float dY = newCoord.y-self.panCoord.y;
gesture.view.frame = CGRectMake(gesture.view.frame.origin.x+dX,
gesture.view.frame.origin.y+dY,
gesture.view.frame.size.width,
gesture.view.frame.size.height);
}
This is just a preference of mine. To me it is a lot simpler using a gesture recognizer then using touchesBegan, touchesEnded, touchesMoved. I will use these in cases where the UIPanGestureRecognizer will not work.
This is work code for move UIView objects
float oldX, oldY;
BOOL dragging;
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [[event allTouches] anyObject];
CGPoint touchLocation = [touch locationInView:touch.view];
if ([[touch.view class] isSubclassOfClass:[UILabel class]]) {
dragging = YES;
oldX = touchLocation.x;
oldY = touchLocation.y;
}
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
dragging = NO;
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [[event allTouches] anyObject];
CGPoint touchLocation = [touch locationInView:touch.view];
if ([[touch.view class] isSubclassOfClass:[UILabel class]]) {
UILabel *label = (UILabel *)touch.view;
if (dragging) {
CGRect frame = label.frame;
frame.origin.x = label.frame.origin.x + touchLocation.x - oldX;
frame.origin.y = label.frame.origin.y + touchLocation.y - oldY;
label.frame = frame;
}
}
}
There's something a bit strange in your code.
You ask for the location in self, some UIView which presumably contains the UILabel you want to check. This begs the question as to why you do not add the touchesXXX to some UILabel subclass of yours.
This cancels out as you're using the label.frame which is defined in terms of its superview.bounds (your parent UIView you asked the touch location with respect to), but this doesn't make for the most straightforward way to follow what's going on.
With this code i could move my UIView Object anywhere.
My ViewController.swift looks like this.
// code from below
import UIKit
class ViewController: UIViewController {
var location = CGPoint(x: 0, y: 0)
#IBOutlet weak var Person: UIImageView!
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
var touch : UITouch! = touches.first! as UITouch
location = touch.location(in: self.view)
Person.center = location
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
var touch : UITouch! = touches.first! as UITouch
location = touch.location(in: self.view)
Person.center = location
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
Person.center = CGPoint(x: 160, y: 330)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
//ends
Hope it helps although it is another way to do it than the one mentioned in question.