So, I have this menu which is this project here but just update to the latest syntax of swift. the problem I have is that every time I load up the scene in which the menu is located in, it starts in the middle of the array instead of at the start of it like so.
What i'm trying to achieve is so when I load up the scene with the menu in it i want it to start at the start of array instead of the middle of the array the main functions that place and position the spritenodes in the scene are as follows:
func createPlayers() {
for _ in 0..<9 {
let player = SKSpriteNode(color: UIColor.blue , size: CGSize(width: 100, height: 200))
players.append(player)
}
}
func placePlayersOnPositions() {
for i in 0..<players.count/2 {
players[i].position = CGPoint(x: leftGuide, y: size.height/2)
}
players[players.count/2].position = CGPoint(x: size.width/2, y: size.height/2)
for i in players.count/2 + 1..<players.count {
players[i].position = CGPoint(x: rightGuide, y: size.height/2)
}
for player in players {
player.setScale(calculateScaleForX(x: player.position.x))
self.addChild(player)
}
}
How can I modify the above code so that it starts the menu at the start of the array instead of in the middle like this:
Here is the full menu code:
class GameScene: SKScene {
enum Zone {
case Left, Center, Right
}
var players = [SKSpriteNode]()
var leftPlayer: SKSpriteNode?
var centerPlayer: SKSpriteNode?
var rightPlayer: SKSpriteNode?
var leftGuide : CGFloat {
return round(view!.bounds.width / 6.0)
}
var rightGuide : CGFloat {
return view!.bounds.width - leftGuide
}
var gap : CGFloat {
return (size.width / 2 - leftGuide) / 2
}
// Initialization
override init(size: CGSize) {
super.init(size:size)
createPlayers()
centerPlayer = players[players.count/2]
setLeftAndRightPlayers()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func didMove(to view: SKView) {
placePlayersOnPositions()
calculateZIndexesForPlayers()
}
func createPlayers() {
for _ in 0..<9 {
let player = SKSpriteNode(color: UIColor.blue , size: CGSize(width: 100, height: 200))
players.append(player)
}
}
func placePlayersOnPositions() {
for i in 0..<players.count/2 {
players[i].position = CGPoint(x: leftGuide, y: size.height/2)
}
players[players.count/2].position = CGPoint(x: size.width/2, y: size.height/2)
for i in players.count/2 + 1..<players.count {
players[i].position = CGPoint(x: rightGuide, y: size.height/2)
}
for player in players {
player.setScale(calculateScaleForX(x: player.position.x))
self.addChild(player)
}
}
// Helper functions
func calculateScaleForX(x:CGFloat) -> CGFloat {
let minScale = CGFloat(0.5)
if x <= leftGuide || x >= rightGuide {
return minScale
}
if x < size.width/2 {
let a = 1.0 / (size.width - 2 * leftGuide)
let b = 0.5 - a * leftGuide
return (a * x + b)
}
let a = 1.0 / (frame.size.width - 2 * rightGuide)
let b = 0.5 - a * rightGuide
return (a * x + b)
}
func calculateZIndexesForPlayers() {
var playerCenterIndex : Int = players.count / 2
for i in 0..<players.count {
if centerPlayer == players[i] {
playerCenterIndex = i
}
}
for i in 0...playerCenterIndex {
players[i].zPosition = CGFloat(i)
}
for i in playerCenterIndex+1..<players.count {
players[i].zPosition = centerPlayer!.zPosition * 2 - CGFloat(i)
}
}
func movePlayerToX(player: SKSpriteNode, x: CGFloat, duration: TimeInterval) {
let moveAction = SKAction.moveTo(x: x, duration: duration)
let scaleAction = SKAction.scale(to: calculateScaleForX(x: x), duration: duration)
player.run(SKAction.group([moveAction, scaleAction]))
}
func movePlayerByX(player: SKSpriteNode, x: CGFloat) {
let duration = 0.01
if player.frame.midX <= rightGuide && player.frame.midX >= leftGuide {
player.run(SKAction.moveBy(x: x, y: 0, duration: duration), completion: {
player.setScale(self.calculateScaleForX(x: player.frame.midX))
})
if player.frame.midX < leftGuide {
player.position = CGPoint(x: leftGuide, y: player.position.y)
} else if player.frame.midX > rightGuide {
player.position = CGPoint(x: rightGuide, y: player.position.y)
}
}
}
func zoneOfCenterPlayer() -> Zone {
let gap = size.width / 2 - leftGuide
switch centerPlayer!.frame.midX {
case let x where x < leftGuide + gap/2:
return .Left
case let x where x > rightGuide - gap/2:
return .Right
default: return .Center
}
}
func setLeftAndRightPlayers() {
var playerCenterIndex : Int = players.count / 2
for i in 0..<players.count {
if centerPlayer == players[i] {
playerCenterIndex = i
}
}
if playerCenterIndex > 0 && playerCenterIndex < players.count {
leftPlayer = players[playerCenterIndex-1]
} else {
leftPlayer = nil
}
if playerCenterIndex > -1 && playerCenterIndex < players.count-1 {
rightPlayer = players[playerCenterIndex+1]
} else {
rightPlayer = nil
}
}
// Touch interactions
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch: UITouch = touches.first!
let node = self.atPoint(touch.location(in: self))
if node == centerPlayer {
let fadeOut = SKAction.fadeAlpha(to: 0.5, duration: 0.15)
let fadeIn = SKAction.fadeAlpha(to: 1, duration: 0.15)
centerPlayer!.run(fadeOut, completion: { self.centerPlayer!.run(fadeIn) })
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
let duration = 0.01
let touch: UITouch = touches.first!
let newPosition = touch.location(in: self)
let oldPosition = touch.previousLocation(in: self)
let xTranslation = newPosition.x - oldPosition.x
if centerPlayer!.frame.midX > size.width/2 {
if (leftPlayer != nil) {
let actualTranslation = leftPlayer!.frame.midX + xTranslation > leftGuide ? xTranslation : leftGuide - leftPlayer!.frame.midX
movePlayerByX(player: leftPlayer!, x: actualTranslation)
}
} else {
if (rightPlayer != nil) {
let actualTranslation = rightPlayer!.frame.midX + xTranslation < rightGuide ? xTranslation : rightGuide - rightPlayer!.frame.midX
movePlayerByX(player: rightPlayer!, x: actualTranslation)
}
}
movePlayerByX(player: centerPlayer!, x: xTranslation)
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
//func touchesEnded(touches: NSSet, withEvent event: UIEvent) {
let touch: UITouch = touches.first!
let duration = 0.25
switch zoneOfCenterPlayer() {
case .Left:
if (rightPlayer != nil) {
movePlayerToX(player: centerPlayer!, x: leftGuide, duration: duration)
if (leftPlayer != nil) {
movePlayerToX(player: leftPlayer!, x: leftGuide, duration: duration)
}
if (rightPlayer != nil) {
movePlayerToX(player: rightPlayer!, x: size.width/2, duration: duration)
}
centerPlayer = rightPlayer
setLeftAndRightPlayers()
} else {
movePlayerToX(player: centerPlayer!, x: size.width/2, duration: duration)
}
case .Right:
if (leftPlayer != nil) {
movePlayerToX(player: centerPlayer!, x: rightGuide, duration: duration)
if (rightPlayer != nil) {
movePlayerToX(player: rightPlayer!, x: rightGuide, duration: duration)
}
if (leftPlayer != nil) {
movePlayerToX(player: leftPlayer!, x: size.width/2, duration: duration)
}
centerPlayer = leftPlayer
setLeftAndRightPlayers()
} else {
movePlayerToX(player: centerPlayer!, x: size.width/2, duration: duration)
}
case .Center:
movePlayerToX(player: centerPlayer!, x: size.width/2, duration: duration)
if (leftPlayer != nil) {
movePlayerToX(player: leftPlayer!, x: leftGuide, duration: duration)
}
if (rightPlayer != nil) {
movePlayerToX(player: rightPlayer!, x: rightGuide, duration: duration)
}
}
calculateZIndexesForPlayers()
}
}
I had just a quick peek and did not try it, but this might work
func placePlayersOnPositions() {
players.first?.position = CGPoint(x: size.width/2, y: size.height/2)
if players.count > 1 {
for i in 1..<players.count {
players[i].position = CGPoint(x: rightGuide, y: size.height/2)
}
}
for player in players {
player.setScale(calculateScaleForX(x: player.position.x))
self.addChild(player)
}
}
For starters you will have to change your init function to not hard code the center player to count divided by 2 but rather to player zero.
from
centerPlayer = players[players.count/2]
to
centerPlayer = players[0]
Related
I'm working on a 2-d Platformer style game. I'm very new to IOS and Swift development. I'm attempting to use a button (a different node) to move my character across the screen from left to right. It works fine until I reach the halfway point then it speeds up dramatically and letting go of the touch of the button doesn't always stop it. Sometimes it requires another touch. Also the background doesn't appear to keep up with the player. Once the player hits mid screen the background should shift as the player continues moving.
I've pieced what I've done together from multiple tutorials SO examples but I'm stuck on this point.
class StoryModeScene: SKScene, SKPhysicsContactDelegate {
var tileMap = JSTileMap(named: "legend1Level1.tmx")
var tileSize:CGSize!
var xPointsToMovePerSecond:CGFloat = 0
var rightMoveButton = SKSpriteNode(imageNamed: "right-move")
var leftMoveButton = SKSpriteNode(imageNamed: "left-move")
var jumpButton = SKSpriteNode(imageNamed: "a-button")
var fireButton = SKSpriteNode(imageNamed: "b-button")
var forwardMarch:Bool = false
var mightAsWellJump:Bool = false
var onGround:Bool = true
//CREATE THE PLAYER ATLAS FOR ANIMATION
let playerAtlas = SKTextureAtlas(named:"legend1")
var playerSprites = Array<Any>()
var player = SKSpriteNode(imageNamed: "legend1")
var repeatActionPlayer = SKAction()
override func didMove(to view: SKView) {
/* Setup your scene here */
setupScene()
addPlayer()
//PREPARE TO ANIMATE THE PLAYER AND REPEAT THE ANIMATION FOREVER
let animatedPlayer = SKAction.animate(with: self.playerSprites as! [SKTexture], timePerFrame: 0.1)
self.repeatActionPlayer = SKAction.repeatForever(animatedPlayer)
leftMoveButton.position.x = 64
leftMoveButton.position.y = 64
leftMoveButton.name = "moveLeft"
addChild(leftMoveButton)
rightMoveButton.position.x = 124
rightMoveButton.position.y = 64
rightMoveButton.name = "moveRight"
addChild(rightMoveButton)
jumpButton.position.x = 771
jumpButton.position.y = 64
jumpButton.name = "jumpButton"
addChild(jumpButton)
fireButton.position.x = 836
fireButton.position.y = 64
fireButton.name = "fireButton"
addChild(fireButton)
}
override func update(_ currentTime: TimeInterval) {
if (forwardMarch) {
//let moveAction = SKAction.moveBy(x: 3, y: 0, duration: 1)
//let repeatForEver = SKAction.repeatForever(moveAction)
//let seq = SKAction.sequence([moveAction, repeatForEver])
//run the action on your ship
//player.run(seq)
player.position.x = player.position.x + 3
setViewpointCenter(player.position)
}
if (mightAsWellJump) {
let jumpForce = CGPoint(x: 0.0, y: 310.0)
let jumpCutoff: Float = 150.0
if mightAsWellJump && onGround {
player.physicsBody!.velocity = CGVector(dx: player.physicsBody!.velocity.dx + jumpForce.x, dy: player.physicsBody!.velocity.dy + jumpForce.y)
onGround = false
} else if !mightAsWellJump && player.physicsBody!.velocity.dy > CGFloat(jumpCutoff) {
player.physicsBody!.velocity = CGVector(dx: player.physicsBody!.velocity.dx, dy: CGFloat(jumpCutoff))
}
player.position = CGPoint(x: player.position.x, y: player.position.y + 5);
}
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in (touches) {
let positionInScene = touch.location(in: self)
let touchedNode = self.atPoint(positionInScene)
if let name = touchedNode.name {
if name == "jumpButton" {
mightAsWellJump = true
player.texture = SKTexture(imageNamed: "legend1_jump")
}
if name == "moveRight" {
forwardMarch = true
self.player.run(repeatActionPlayer)
}
}
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch = touches.first!
if rightMoveButton.contains(touch.location(in: self)) {
forwardMarch = false
player.removeAllActions()
player.texture = SKTexture(imageNamed: "legend1")
}
if jumpButton.contains(touch.location(in: self)) {
mightAsWellJump = false
player.removeAllActions()
player.texture = SKTexture(imageNamed: "legend1")
}
}
func setViewpointCenter(_ position: CGPoint) {
var x = max(position.x, size.width / 2)
var y = max(position.y, size.height / 2)
x = min(x, (tileMap!.mapSize.width * tileMap!.tileSize.width) - size.width / 2)
y = min(y, (tileMap!.mapSize.height * tileMap!.tileSize.height) - size.height / 2)
let actualPosition = CGPoint(x: CGFloat(x), y: CGFloat(y))
let centerOfView = CGPoint(x: size.width / 2, y: size.height / 2)
let viewPoint = CGPoint(x: (centerOfView.x - actualPosition.x) * 3, y: centerOfView.y - actualPosition.y)
tileMap!.position = viewPoint
}
func setupScene() {
playerSprites.append(playerAtlas.textureNamed("legend1_0"))
playerSprites.append(playerAtlas.textureNamed("legend1_1"))
playerSprites.append(playerAtlas.textureNamed("legend1_2"))
playerSprites.append(playerAtlas.textureNamed("legend1_3"))
playerSprites.append(playerAtlas.textureNamed("legend1_4"))
playerSprites.append(playerAtlas.textureNamed("legend1_5"))
backgroundColor = UIColor(red: 165.0/255.0, green: 216.0/255.0, blue: 255.0/255.0, alpha: 1.0)
physicsWorld.gravity = CGVector(dx: 0, dy: -9.8)
anchorPoint = CGPoint(x: 0, y: 0)
position = CGPoint(x: 0, y: 0)
let point = tileMap!.calculateAccumulatedFrame()
print (point)
tileMap!.position = CGPoint(x: 0, y: 0)
addChild(tileMap!)
addFloor()
}
func addFloor() {
for a in 0..<Int(tileMap!.mapSize.width) {
for b in 0..<Int(tileMap!.mapSize.height) {
let layerInfo:TMXLayerInfo = tileMap!.layers.firstObject as! TMXLayerInfo
let point = CGPoint(x: a, y: b)
let walls = tileMap!.layerNamed("Walls")
let wallInfo:TMXLayerInfo = walls!.layerInfo
let wallGIDs = wallInfo.layer.tileGid(at: wallInfo.layer.point(forCoord: point))
if wallGIDs > 0 {
//print (wallGIDs)
//let node = walls
let node = wallInfo.layer.tile(atCoord: point)
node!.physicsBody = SKPhysicsBody(rectangleOf: node!.size)
node!.physicsBody?.isDynamic = false
}
}
}
}
func addPlayer() {
tileSize = tileMap?.tileSize
player.position = CGPoint(x: tileSize.width + player.size.width/2, y: tileSize.height + player.size.height*8)
let rect = CGRect(origin: CGPoint(x: 0,y :0), size: CGSize(width: 50, height: 95))
player.physicsBody = SKPhysicsBody(rectangleOf: rect.size)
player.physicsBody!.velocity = CGVector(dx: 0.0, dy: 0.0)
player.physicsBody!.isDynamic = true
player.physicsBody!.restitution = 0
player.physicsBody!.allowsRotation = false
player.physicsBody!.friction = 1.0
addChild(player)
}
}
Holding the rightMoveButton should move at a consistent pace to the right. As the player gets to mid screen the view point of the background should shift until it reaches the end of the background at which point the player can move off screen and complete the level. Releasing the button should allow the player to stop.
You can move the scene as the character moves by creating an SKCameraNode. You then can choose when to move the camera to create the right affect. Make sure to set the camera as your game’s camera. You can learn about cameras here. As for the speeding up, I assume it has something to do with your physicsBody. If you’re interested about player movement, you could look here or here. There are many other great videos and sites online, which you could find by searching “SpriteKit player movement.”
Eli Front's answer pointed me in the right direction. I still have some issues with this code but the answer to my question was using SKCamera node as Eli pointed out. I just wanted to post the code that works so if anyone has a similar question there's a code example as far as camera moving with the player. The speed issue was essentially an optical illusion with the background moving too fast.
Note that the controls are children of the camera. That is so they move with the camera as well otherwise as your node moves the controls would move off screen.
import UIKit
import SpriteKit
import GameKit
class StoryModeScene: SKScene, SKPhysicsContactDelegate {
var tileMap = JSTileMap(named: "legend1Level1.tmx")
var tileSize:CGSize!
var xPointsToMovePerSecond:CGFloat = 0
var rightMoveButton = SKSpriteNode(imageNamed: "right-move")
var leftMoveButton = SKSpriteNode(imageNamed: "left-move")
var jumpButton = SKSpriteNode(imageNamed: "a-button")
var fireButton = SKSpriteNode(imageNamed: "b-button")
var forwardMarch:Bool = false
var mightAsWellJump:Bool = false
var onGround:Bool = true
//CREATE THE Player ATLAS FOR ANIMATION
let playerAtlas = SKTextureAtlas(named:"legend1")
var playerSprites = Array<Any>()
var player = SKSpriteNode(imageNamed: "legend1")
var repeatActionPlayer = SKAction()
let cam = SKCameraNode()
var previousUpdateTime: TimeInterval = 0
override func didMove(to view: SKView) {
// SETUP CAMERA
self.camera = cam
scene?.addChild(cam)
cam.position.x = 448
cam.position.y = 212
setupScene()
addPlayer()
//PREPARE TO ANIMATE THE PLAYER AND REPEAT THE ANIMATION FOREVER
let animatedPlayer = SKAction.animate(with: self.playerSprites as! [SKTexture], timePerFrame: 0.1)
self.repeatActionPlayer = SKAction.repeatForever(animatedPlayer)
// SETUP CONTROLS
leftMoveButton.position.x = -338
leftMoveButton.position.y = -112
leftMoveButton.name = "moveLeft"
leftMoveButton.zPosition = 5
cam.addChild(leftMoveButton)
rightMoveButton.position.x = -278
rightMoveButton.position.y = -112
rightMoveButton.name = "moveRight"
cam.addChild(rightMoveButton)
jumpButton.position.x = 278
jumpButton.position.y = -112
jumpButton.name = "jumpButton"
jumpButton.zPosition = 5
cam.addChild(jumpButton)
fireButton.position.x = 338
fireButton.position.y = -112
fireButton.name = "fireButton"
cam.addChild(fireButton)
}
override func update(_ currentTime: TimeInterval) {
if (forwardMarch) {
if player.position.x > 448 && player.position.x < 1800 {
cam.position.x = player.position.x
} else if player.position.x >= 1800 {
cam.position.x = 1800
}
setViewpointCenter(player.position)
player.position.x = player.position.x + 3
}
if (mightAsWellJump) {
let jumpForce = CGPoint(x: 0.0, y: 310.0)
let jumpCutoff: Float = 150.0
if mightAsWellJump && onGround {
player.physicsBody!.velocity = CGVector(dx: player.physicsBody!.velocity.dx + jumpForce.x, dy: player.physicsBody!.velocity.dy + jumpForce.y)
onGround = false
} else if !mightAsWellJump && player.physicsBody!.velocity.dy > CGFloat(jumpCutoff) {
player.physicsBody!.velocity = CGVector(dx: player.physicsBody!.velocity.dx, dy: CGFloat(jumpCutoff))
}
player.position = CGPoint(x: player.position.x, y: player.position.y + 5);
}
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in (touches) {
let positionInScene = touch.location(in: self)
let touchedNode = self.atPoint(positionInScene)
if let name = touchedNode.name {
if name == "jumpButton" {
mightAsWellJump = true
player.texture = SKTexture(imageNamed: "legend1_jump")
}
if name == "moveRight" {
player.physicsBody!.velocity.dy = 0.0
forwardMarch = true
self.player.run(repeatActionPlayer)
}
}
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in (touches) {
let positionInScene = touch.location(in: self)
let touchedNode = self.atPoint(positionInScene)
if let name = touchedNode.name {
if name == "jumpButton" {
mightAsWellJump = false
player.removeAllActions()
player.texture = SKTexture(imageNamed: "legend1")
player.physicsBody!.velocity = CGVector(dx: 0.0, dy: 0.0)
}
if name == "moveRight" {
player.removeAllActions()
player.texture = SKTexture(imageNamed: "legend1")
forwardMarch = false
}
}
}
}
func setViewpointCenter(_ position: CGPoint) {
var x = max(position.x, size.width / 2)
var y = max(position.y, size.height / 2)
x = min(x, (tileMap!.mapSize.width * tileMap!.tileSize.width) - size.width / 2)
y = min(y, (tileMap!.mapSize.height * tileMap!.tileSize.height) - size.height / 2)
let actualPosition = CGPoint(x: CGFloat(x), y: CGFloat(y))
let centerOfView = CGPoint(x: size.width / 2, y: size.height / 2)
let viewPoint = CGPoint(x: centerOfView.x - actualPosition.x, y: centerOfView.y - actualPosition.y)
if (actualPosition.x > 1800) {
tileMap!.position = tileMap!.position
} else {
tileMap!.position = viewPoint
}
}
func setupScene() {
// ADD PLAYER SPRITES #TODO MAKE THIS DYNAMIC
playerSprites.append(playerAtlas.textureNamed("legend1_0"))
playerSprites.append(playerAtlas.textureNamed("legend1_1"))
playerSprites.append(playerAtlas.textureNamed("legend1_2"))
playerSprites.append(playerAtlas.textureNamed("legend1_3"))
playerSprites.append(playerAtlas.textureNamed("legend1_4"))
playerSprites.append(playerAtlas.textureNamed("legend1_5"))
backgroundColor = UIColor(red: 165.0/255.0, green: 216.0/255.0, blue: 255.0/255.0, alpha: 1.0)
anchorPoint = CGPoint(x: 0, y: 0)
position = CGPoint(x: 0, y: 0)
// let point = tileMap!.calculateAccumulatedFrame()
tileMap!.position = CGPoint(x: 0, y: 0)
addChild(tileMap!)
addFloor()
}
func addFloor() {
for a in 0..<Int(tileMap!.mapSize.width) {
for b in 0..<Int(tileMap!.mapSize.height) {
// let layerInfo:TMXLayerInfo = tileMap!.layers.firstObject as! TMXLayerInfo
let point = CGPoint(x: a, y: b)
let walls = tileMap!.layerNamed("Walls")
let wallInfo:TMXLayerInfo = walls!.layerInfo
let wallGIDs = wallInfo.layer.tileGid(at: wallInfo.layer.point(forCoord: point))
if wallGIDs > 0 {
let node = wallInfo.layer.tile(atCoord: point)
node!.physicsBody = SKPhysicsBody(rectangleOf: node!.size)
node!.physicsBody?.isDynamic = false
}
}
}
}
func addPlayer() {
tileSize = tileMap?.tileSize
player.position = CGPoint(x: tileSize.width + player.size.width/2, y: tileSize.height + player.size.height*8)
let rect = CGRect(origin: CGPoint(x: 0,y :0), size: CGSize(width: 50, height: 95))
player.physicsBody = SKPhysicsBody(rectangleOf: rect.size)
player.physicsBody!.velocity = CGVector(dx: 0.0, dy: 0.0)
player.physicsBody!.isDynamic = true
player.physicsBody!.restitution = 0
player.physicsBody!.allowsRotation = false
player.physicsBody!.friction = 1.0
player.physicsBody!.mass = 1.0
addChild(player)
}
}
As I mentioned I still have some issues like the player node floating/flying when I jump/run at the same time. The 1st jump is the correct height and all subsequent jumps are smaller etc but I'm working on figuring those out.
I am programming something like PONG in IOS Playgrounds.
It is all working but it is crashing after a few seconds.
I don't really know why. Can someone please help me?
The Position of the Ball is changing and The Touch Event is working but it crashes in the draw Function of the UIView
I tried to turn clear Context and all but it didn't work
import UIKit;
import PlaygroundSupport;
import CoreGraphics;
import Dispatch;
func delay (delay:Double)
{
let t = CACurrentMediaTime()
var b = true
while b {
if(CACurrentMediaTime()>t+delay){
b=false;
return
}
}
}
let page = PlaygroundPage.current
var sheight:Double = 700 //Double(UIScreen.main.bounds.height);
var swidth:Double = 900 //Double(UIScreen.main.bounds.width);
let v = UIView(frame: CGRect(x: -100, y: -100, width: swidth, height: sheight))
swidth = Double(v.bounds.width)
sheight = Double(v.bounds.height)
let moveStab = sheight / 300;
var dirX = 1.0
var dirY = 1.0
let bsp:Double = 3.0
var Point1 = 0;
var Point2 = 0;
let psp:Double = 2.0
var up = [false,false]
var down = [false,false]
var ballx = swidth / 2;
var bally = sheight / 2;
let ballRad = 30.0
var p1x = 20.0
var p1y = sheight / 2
var p2x = swidth - 40.0
var p2y = sheight / 2
let pw = 20.0
let ph = 75.0
class Draw: UIView {
func PointInRad (point: CGPoint, point2 : CGPoint, rad : CGFloat) -> Bool{
let x1 = point.x;
let y1 = point.y
let x2 = point2.x
let y2 = point2.y
var dx = x1-x2
var dy = y1 - y2
if (dx < 0){dx=dx * -1}
if (dy < 0){dy=dy * -1}
let e = sqrt(dx*dx + dy*dy)
if(e <= rad){
return true
}
return false
}
func PointInRad (point: CGPoint, x2 : CGFloat, y2: CGFloat, rad: CGFloat) -> Bool {
let x1 = point.x;
let y1 = point.y
var dx = x1-x2
var dy = y1 - y2
if (dx < 0){dx=dx * -1}
if (dy < 0){dy=dy * -1}
let e = sqrt(dx*dx + dy*dy)
if(e <= rad){
return true
}
return false
}
func PointInRad ( x1 : CGFloat, y1 : CGFloat, x2 : CGFloat, y2 : CGFloat, rad: CGFloat) -> Bool{
var dx = x1-x2
var dy = y1 - y2
if (dx < 0){dx=dx * -1}
if (dy < 0){dy=dy * -1}
let e = sqrt(dx*dx + dy*dy)
if(e <= rad){
return true
}
return false
}
func update(){
self.isMultipleTouchEnabled = true;
self.clearsContextBeforeDrawing = true;
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
if(PointInRad(point: touch.location(in: view), x2: 700, y2:400 , rad: 50)){
up[1] = true
}
if(PointInRad(point: touch.location(in: view), x2: 100, y2:400 , rad: 50)){
up[0] = true
}
if(PointInRad(point: touch.location(in: view), x2: 700, y2:500 , rad: 50)){
down[1] = true
}
if(PointInRad(point: touch.location(in: view), x2: 100, y2:500 , rad: 50)){
down[0] = true
}
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
if(!PointInRad(point: touch.location(in: view), x2: 700, y2:400 , rad: 50)){
up[1] = false
}
if(!PointInRad(point: touch.location(in: view), x2: 100, y2:400 , rad: 50)){
up[0] = false
}
if(!PointInRad(point: touch.location(in: view), x2: 700, y2:500 , rad: 50)){
down[1] = false
}
if(!PointInRad(point: touch.location(in: view), x2: 100, y2:500 , rad: 50)){
down[0] = false
}}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
if(PointInRad(point: touch.location(in: view), x2: 700, y2:400 , rad: 50)){
up[1] = false
}
if(PointInRad(point: touch.location(in: view), x2: 100, y2:400 , rad: 50)){
up[0] = false
}
if(PointInRad(point: touch.location(in: view), x2: 700, y2:500 , rad: 50)){
down[1] = false
}
if(PointInRad(point: touch.location(in: view), x2: 100, y2:500 , rad: 50)){
down[0] = false
}}
}
override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
touchesEnded(touches, with: event)
self.setNeedsDisplay()
}
var n = false;
func drawCircle (color : UIColor, x:CGFloat, y:CGFloat, rad: CGFloat){
// Circle Drawing
let newx = x - rad / 2
let newy = y - rad / 2
let circlePath = UIBezierPath(ovalIn: CGRect(x: newx, y: newy, width: rad, height: rad))
color.setFill()
circlePath.fill()
}
func drawRect ( color : UIColor, x:CGFloat, y:CGFloat, width: CGFloat, height: CGFloat ) {
let circlePath = UIBezierPath(rect: CGRect(x: x, y: y, width: width, height: height))
color.setFill()
circlePath.fill()
}
override func draw(_: CGRect) {
var Path = UIBezierPath(ovalIn: CGRect(x: -100, y: -100, width: 900, height: 700))
let con = UIGraphicsGetCurrentContext();
con?.clear(CGRect(x: -100, y: -100, width: 1000, height: 1000))
// Color Declarations
var color = UIColor.white;
let ButtonN = UIColor(white: 0.5, alpha: 0.5)
let ButtonT = UIColor(white: 0.8, alpha: 0.8)
var p1 = NSString(string: String(Point1))
p1.draw(in: CGRect(x: 325, y: 100, width: 75, height: 100), withAttributes: [NSAttributedString.Key.foregroundColor: UIColor.white,NSAttributedString.Key.font: UIFont(name: "Arial", size: 80) ])
p1 = NSString(string: String(Point2))
p1.draw(in: CGRect(x: 430, y: 100, width: 75, height: 100), withAttributes: [NSAttributedString.Key.foregroundColor: UIColor.white,NSAttributedString.Key.font: UIFont(name: "Arial", size: 80) ])
p1 = NSString(string: String("Pong by Jakob"))
p1.draw(in: CGRect(x: 700, y: 25, width: 100, height: 100), withAttributes: [NSAttributedString.Key.foregroundColor: UIColor.white,NSAttributedString.Key.font: UIFont(name: "Arial", size: 20) ])
var rad = CGFloat(50);
var newx:CGFloat = 100.0
var newy:CGFloat = 400.0
var circlePath = UIBezierPath(ovalIn: CGRect(x: newx, y: newy, width: rad, height: rad))
if up[0] {
ButtonT.setFill();
} else {
ButtonN.setFill();
}
circlePath.fill()
circlePath = UIBezierPath(ovalIn: CGRect(x: 700, y: newy, width: rad, height: rad))
if up[1] {
ButtonT.setFill();
} else {
ButtonN.setFill();
}
circlePath.fill()
circlePath = UIBezierPath(ovalIn: CGRect(x: newx, y: 500, width: rad, height: rad))
if down[0] {
ButtonT.setFill();
} else {
ButtonN.setFill();
}
circlePath.fill()
circlePath = UIBezierPath(ovalIn: CGRect(x: 700, y: 500, width: rad, height: rad))
if down[1] {
ButtonT.setFill();
} else {
ButtonN.setFill();
}
circlePath.fill()
for i in 0 ... 30 {
color = UIColor.white
circlePath = UIBezierPath(rect: CGRect(x: CGFloat(393), y:CGFloat(Double(i)*50), width: CGFloat(14), height: CGFloat(30)))
color.setFill()
circlePath.fill()
}
circlePath = UIBezierPath(rect: CGRect(x: p1x, y: p1y, width: pw, height: ph))
color.setFill()
circlePath.fill()
circlePath = UIBezierPath(rect: CGRect(x: p2x, y: p2y, width: pw, height: ph))
color.setFill()
circlePath.fill()
rad = CGFloat(ballRad);
color = UIColor.white;
newx = (CGFloat(ballx) - rad / 2)
newy = CGFloat(bally) - rad / 2
circlePath = UIBezierPath(ovalIn: CGRect(x: newx, y: newy, width: rad, height: rad))
color.setFill()
circlePath.fill()
}
}
let view = Draw(frame: CGRect(x: 0, y: 0, width: swidth, height: sheight))
view.setNeedsDisplay()
view.update()
let T = Thread(block: {
while true {
if(Int(ballx)<Int(p1x + pw) && bally+ballRad <= p1y + ph && bally - ballRad >= p1y){
dirX *= -1
}
if(ballx<p2x && bally+ballRad <= p2y + ph && bally - ballRad >= p2y){
dirX *= -1
}
if(bally < 0 + ballRad || bally > 600 - ballRad){
dirY *= -1;
}
if (ballx - ballRad < 0){
ballx = 800/2
dirX = 1;
Point2+=1
}
if (ballx + ballRad > 800){
ballx = swidth/2
dirX = -1;
Point1+=1
}
if up[0] {
p1y -= psp
}
if up[1]{
p2y -= psp
}
if down[0] {
p1y += psp
}
if down[1]{
p2y += psp
}
ballx += bsp * dirX
bally += bsp * dirY
view.setNeedsDisplay()
view.updateFocusIfNeeded()
for i in 1 ... 10000 {
print(i)
}
}
})
T.start()
let t = Thread(block: {
while true {
DispatchQueue.main.sync {
view .setNeedsDisplay()
}}
})
t.start()
page.liveView = view;
page.needsIndefiniteExecution = true
page.wantsFullScreenLiveView = true
Because i am on my IPad i dont have any Error message
I'm trying to make a 'Pop the Lock' like game for a bit of fun and practice, but my SKSpriteNodes are not appearing at all. My GameScene.swift is below as well as my GameViewController.swift. I've looked at other people's projects using Sprite Kit and they all work fine but their code in mine just isn't working either.
import UIKit
import SpriteKit
import GameplayKit
class GameViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
if let view = self.view as! SKView? {
if let scene = SKScene(fileNamed: "GameScene") {
scene.scaleMode = .aspectFill
view.presentScene(scene)
}
view.ignoresSiblingOrder = true
view.showsFPS = true
view.showsNodeCount = true
}
}
override var shouldAutorotate: Bool {
return true
}
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
if UIDevice.current.userInterfaceIdiom == .phone {
return .allButUpsideDown
} else {
return .all
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
override var prefersStatusBarHidden: Bool {
return true
}
}
import SpriteKit
import GameplayKit
class GameScene: SKScene {
var circle = SKSpriteNode()
var tapper = SKSpriteNode()
var tapperPath = UIBezierPath()
var gameStarted = Bool()
var movingClockwise = Bool()
override func didMove(to view: SKView) {
circle = SKSpriteNode(imageNamed: "Circle")
circle.size = CGSize(width: 300, height: 300)
circle.position = CGPoint(x: UIScreen.main.bounds.width/2, y: UIScreen.main.bounds.height/2)
self.addChild(circle)
tapper = SKSpriteNode(imageNamed: "Tapper")
tapper.size = CGSize(width: 40, height: 7)
tapper.position = CGPoint(x: self.frame.width / 2, y: self.frame.height / 2 + 120)
tapper.zRotation = 3.14 / 2
self.addChild(tapper)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if gameStarted {
if movingClockwise {
moveCounterClockWise()
movingClockwise = false
} else {
moveClockWise()
movingClockwise = true
}
} else {
moveClockWise()
movingClockwise = true
gameStarted = true
}
}
func moveClockWise() {
let dx = tapper.position.x - self.frame.width / 2
let dy = tapper.position.y - self.frame.height / 2
let rad = atan2(dy, dx)
tapperPath = UIBezierPath(arcCenter: CGPoint(x: self.frame.width / 2, y: self.frame.height / 2), radius: 120, startAngle: rad, endAngle: rad + (CGFloat.pi * 4), clockwise: true)
let follow = SKAction.follow(tapperPath.cgPath, asOffset: false, orientToPath: true, speed: 200)
tapper.run(SKAction.repeatForever(follow).reversed())
}
func moveCounterClockWise() {
let dx = tapper.position.x - self.frame.width / 2
let dy = tapper.position.y - self.frame.height / 2
let rad = atan2(dy, dx)
tapperPath = UIBezierPath(arcCenter: CGPoint(x: self.frame.width / 2, y: self.frame.height / 2), radius: 120, startAngle: rad, endAngle: rad + (CGFloat.pi * 4), clockwise: true)
let follow = SKAction.follow(tapperPath.cgPath, asOffset: false, orientToPath: true, speed: 200)
tapper.run(SKAction.repeatForever(follow))
}
override func update(_ currentTime: TimeInterval) {
}
}
Edit
.sks file:
I have a pong game in a Swift Playground that I made by following a tutorial online but my assistant editor won't show anything! Where is my pong game!? My code is below the image, which shows that nothing is showing up.
import SpriteKit
import PlaygroundSupport
// Declare some global constants
let width = 800 as CGFloat
let height = 1200 as CGFloat
let racketHeight = 150 as CGFloat
let ballRadius = 20 as CGFloat
// Three types of collision objects possible
enum CollisionTypes: UInt32 {
case Ball = 1
case Wall = 2
case Racket = 4
}
// Racket direction
enum Direction: Int {
case None = 0
case Up = 1
case Down = 2
}
// Make a SpriteKit scene
class gameScene: SKScene, SKPhysicsContactDelegate {
let racketSpeed = 500.0
var direction = Direction.None
var score = 0
var gameRunning = false
// Screen elements
var racket: SKShapeNode?
var ball: SKShapeNode?
let scoreLabel = SKLabelNode()
// Initialize objects during first start
override func sceneDidLoad() {
super.sceneDidLoad()
scoreLabel.fontSize = 40
scoreLabel.position = CGPoint(x: width/2, y: height - 100)
self.addChild(scoreLabel)
createWalls()
createBall(position: CGPoint(x: width / 2, y: height / 2))
createRacket()
startNewGame()
self.physicsWorld.contactDelegate = self
}
// Create the ball sprite
func createBall(position: CGPoint) {
let physicsBody = SKPhysicsBody(circleOfRadius: ballRadius)
ball = SKShapeNode(circleOfRadius: ballRadius)
physicsBody.categoryBitMask = CollisionTypes.Ball.rawValue
physicsBody.collisionBitMask = CollisionTypes.Wall.rawValue | CollisionTypes.Ball.rawValue | CollisionTypes.Racket.rawValue
physicsBody.affectedByGravity = false
physicsBody.restitution = 1
physicsBody.linearDamping = 0
physicsBody.velocity = CGVector(dx: -500, dy: 500)
ball!.physicsBody = physicsBody
ball!.position = position
ball!.fillColor = SKColor.white
}
// Create the walls
func createWalls() {
createWall(rect: CGRect(origin: CGPoint(x: 0, y: 0), size: CGSize(width: ballRadius, height: height)))
createWall(rect: CGRect(origin: CGPoint(x: 0, y: 0), size: CGSize(width: width, height: ballRadius)))
createWall(rect: CGRect(origin: CGPoint(x: 0, y: height - ballRadius), size: CGSize(width: width, height: ballRadius)))
}
func createWall(rect: CGRect) {
let node = SKShapeNode(rect: rect)
node.fillColor = SKColor.white
node.physicsBody = getWallPhysicsbody(rect: rect)
self.addChild(node)
}
// Create the physics objetcs to handle wall collisions
func getWallPhysicsbody(rect: CGRect) -> SKPhysicsBody {
let physicsBody = SKPhysicsBody(rectangleOf: rect.size, center: CGPoint(x: rect.midX, y: rect.midY))
physicsBody.affectedByGravity = false
physicsBody.isDynamic = false
physicsBody.collisionBitMask = CollisionTypes.Ball.rawValue
physicsBody.categoryBitMask = CollisionTypes.Wall.rawValue
return physicsBody
}
// Create the racket sprite
func createRacket() {
racket = SKShapeNode(rect: CGRect(origin: CGPoint.zero, size: CGSize(width: ballRadius, height: racketHeight)))
self.addChild(racket!)
racket!.fillColor = SKColor.white
let physicsBody = SKPhysicsBody(rectangleOf: racket!.frame.size, center: CGPoint(x: racket!.frame.midX, y: racket!.frame.midY))
physicsBody.affectedByGravity = false
physicsBody.isDynamic = false
physicsBody.collisionBitMask = CollisionTypes.Ball.rawValue
physicsBody.categoryBitMask = CollisionTypes.Racket.rawValue
physicsBody.contactTestBitMask = CollisionTypes.Ball.rawValue
racket!.physicsBody = physicsBody
}
// Start a new game
func startNewGame() {
score = 0
scoreLabel.text = "0"
racket!.position = CGPoint(x: width - ballRadius * 2, y: height / 2)
let startLabel = SKLabelNode(text: "Game Over")
startLabel.position = CGPoint(x: width / 2, y: height / 2)
startLabel.fontSize = 160
self.addChild(startLabel)
// Animated countdown
let fadeIn = SKAction.fadeIn(withDuration: 0.5)
let fadeOut = SKAction.fadeOut(withDuration: 0.5)
startLabel.text = "3"
startLabel.run(SKAction.sequence([fadeIn, fadeOut]), completion: {
startLabel.text = "2"
startLabel.run(SKAction.sequence([fadeIn, fadeOut]), completion: {
startLabel.text = "1"
startLabel.run(SKAction.sequence([fadeIn, fadeOut]), completion: {
startLabel.text = "0"
startLabel.run(SKAction.sequence([fadeIn, fadeOut]), completion: {
startLabel.removeFromParent()
self.gameRunning = true
self.ball!.position = CGPoint(x: 30, y: height / 2)
self.addChild(self.ball!)
})
})
})
})
}
// Handle touch events to move the racket
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
let location = touch.location(in: self)
if location.y > height / 2 {
direction = Direction.Up
} else if location.y < height / 2{
direction = Direction.Down
}
}
}
// Stop racket movement
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
direction = Direction.None
}
// Game loop:
var dt = TimeInterval(0)
override func update(_ currentTime: TimeInterval) {
if gameRunning {
super.update(currentTime)
checkGameOver()
if dt > 0 {
moveRacket(dt: currentTime - dt)
}
dt = currentTime
}
}
// Move the racket up or down
func moveRacket(dt: TimeInterval) {
if direction == Direction.Up && racket!.position.y < height - racketHeight {
racket!.position.y = racket!.position.y + CGFloat(racketSpeed * dt)
} else if direction == Direction.Down && racket!.position.y > 0 {
racket!.position.y = racket!.position.y - CGFloat(racketSpeed * dt)
}
}
// Check if the ball is still on screen
// Game Over animation
func checkGameOver() {
if ball!.position.x > CGFloat(width) {
gameRunning = false
ball!.removeFromParent()
let gameOverLabel = SKLabelNode(text: "Game Over")
gameOverLabel.position = CGPoint(x: width / 2, y: height / 2)
gameOverLabel.fontSize = 80
self.addChild(gameOverLabel)
// Game Over animation
let rotateAction = SKAction.rotate(byAngle: CGFloat(M_PI), duration: 1)
let fadeInAction = SKAction.fadeIn(withDuration: 2)
gameOverLabel.run(SKAction.repeat(rotateAction, count: 2))
gameOverLabel.run(SKAction.scale(to: 0, duration: 2.5), completion: {
gameOverLabel.removeFromParent()
self.startNewGame()
})
}
}
// Detect collisions between ball and racket to increase the score
func didBegin(_ contact: SKPhysicsContact) {
if contact.bodyA.categoryBitMask == CollisionTypes.Racket.rawValue || contact.bodyB.categoryBitMask == CollisionTypes.Racket.rawValue {
score += 1
scoreLabel.text = String(score)
}
}
}
//Initialize the playground and start the scene:
let skView = SKView(frame: CGRect(origin: CGPoint.zero, size: CGSize(width: width, height: height)))
let scene = gameScene(size: skView.frame.size)
skView.presentScene(scene)
PlaygroundPage.current.liveView = skView
This happens to me sometimes. Playgrounds with SpriteKit just would not show anything in the Timeline view - you do have the Timeline view open, right? If you don't, tap the "Show the Assistant editor" button on the toolbar to open the Timeline view.
If you have the Timeline view open and nothing shows, try shutting down Xcode and restarting it. That generally resolves this issue for me.
How to draw resizable rectangle in UIView , i did many search on google and github and i found this one Click Here using swift 2.3 and i converted it to swift 3 .. but i can't resize rectangle after drawing it and thats the code
//
// ResizableRectangleView.swift
// DrawShapes
//
// Created by Jordan Focht on 3/9/15.
// Copyright (c) 2015 Jordan Focht. All rights reserved.
//
import Foundation
import UIKit
private let DefaultTint = UIColor(red: 0, green: 164 / 255.0, blue: 1.0, alpha: 1.0).cgColor
private let DefaultStrokeTint = UIColor(red: 1.0, green: 1.0, blue: 1.0, alpha: 1.0).cgColor
private let ClearColor = UIColor.clear.cgColor
private let DefaultCircleRadius: CGFloat = 8
private let CornerTouchSize: CGFloat = 44
protocol ResizableRectangleViewDelegate : class {
func didSelectResizableRectangleView(_ view: ResizableRectangleView)
func didDeselectResizableRectangleView(_ view: ResizableRectangleView)
}
class ResizableRectangleView: UIControl {
fileprivate var borderLayer: CALayer = CALayer()
fileprivate var topLeftCircle = CALayer()
fileprivate var topRightCircle = CALayer()
fileprivate var bottomLeftCircle = CALayer()
fileprivate var bottomRightCircle = CALayer()
weak var delegate: ResizableRectangleViewDelegate?
var strokeTintColor: CGColor = DefaultStrokeTint
var circleRadius: CGFloat = DefaultCircleRadius
var nLocation : CGPoint!
override var frame: CGRect {
get {
return super.frame
}
set {
super.frame = newValue
self.updateLayers()
}
}
override var isSelected: Bool {
get {
return super.isSelected
}
set {
let changed = self.isSelected != newValue
super.isSelected = newValue
if changed {
if isSelected {
self.delegate?.didSelectResizableRectangleView(self)
} else {
self.delegate?.didDeselectResizableRectangleView(self)
}
}
}
}
func updateLayers() {
if self.layer.sublayers == nil {
self.layer.addSublayer(self.borderLayer)
self.layer.addSublayer(self.topLeftCircle)
self.layer.addSublayer(self.topRightCircle)
self.layer.addSublayer(self.bottomLeftCircle)
self.layer.addSublayer(self.bottomRightCircle)
let layers = (self.layer.sublayers ?? []) as [CALayer]
for layer in layers {
layer.contentsScale = UIScreen.main.scale
}
}
self.updateBorderLayer()
let circleFrame = self.borderLayer.frame
updateCircleLayer(topLeftCircle, center: CGPoint(x: circleFrame.origin.x, y: circleFrame.origin.y))
updateCircleLayer(topRightCircle, center: CGPoint(x: circleFrame.origin.x, y: circleFrame.maxY))
updateCircleLayer(bottomLeftCircle, center: CGPoint(x: circleFrame.maxX, y: circleFrame.origin.y))
updateCircleLayer(bottomRightCircle, center: CGPoint(x: circleFrame.maxX, y: circleFrame.maxY))
}
func borderedFrame() -> CGRect {
return self.borderLayer.frame
}
// var trackingFrameTransform: ((CGPoint) -> ())?
func moveFrame(_ originalFrame: CGRect, initialTouchLocation: CGPoint, _ location: CGPoint) {
let targetX = originalFrame.origin.x + location.x - initialTouchLocation.x
let targetY = originalFrame.origin.y + location.y - initialTouchLocation.y
let insetBounds = self.insetBounds()
self.frame.origin.x = max(insetBounds.origin.x, min(insetBounds.maxX - self.frame.width, targetX))
self.frame.origin.y = max(insetBounds.origin.y, min(insetBounds.maxY - self.frame.height, targetY))
nLocation = location
}
fileprivate func insetBounds() -> CGRect {
let inset = self.inset()
let contentBounds = (self.superview as? DrawableView)?.contentBounds ?? self.bounds
return contentBounds.insetBy(dx: -inset, dy: -inset)
}
func updateRect(_ anchor: CGPoint, initialTouchLocation: CGPoint, originalCorner: CGPoint , _ location: CGPoint) {
let insetBounds = self.insetBounds()
let locationX = max(insetBounds.origin.x, min(insetBounds.maxX, location.x))
let locationY = max(insetBounds.origin.y, min(insetBounds.maxY, location.y))
let targetX = originalCorner.x + locationX - initialTouchLocation.x
let targetY = originalCorner.y + locationY - initialTouchLocation.y
let minSize = self.inset() + circleRadius
if insetBounds.origin.x < targetX && targetX < insetBounds.maxX {
self.frame.origin.x = min(targetX, anchor.x)
self.frame.size.width = max(minSize * 2, abs(anchor.x - targetX))
}
if insetBounds.origin.y < targetY && targetY < insetBounds.maxY {
self.frame.origin.y = min(targetY, anchor.y)
self.frame.size.height = max(minSize * 2, abs(anchor.y - targetY))
}
nLocation = location
}
override func beginTracking(_ touch: UITouch, with event: UIEvent?) -> Bool {
CATransaction.begin()
CATransaction.setDisableActions(true)
if let superview = self.superview as? DrawableView {
for view in superview.subviews {
if let view = view as? ResizableRectangleView {
if view != self {
view.isSelected = false
view.updateLayers()
}
}
}
superview.bringSubview(toFront: self)
}
let location = touch.location(in: self.superview)
nLocation = location
var anchor: CGPoint?
var corner: CGPoint?
switch (location.x, location.y) {
case (let x, let y) where x < self.frame.origin.x + CornerTouchSize && y < self.frame.origin.y + CornerTouchSize:
anchor = CGPoint(x: self.frame.maxX, y: self.frame.maxY)
corner = CGPoint(x: self.frame.minX, y: self.frame.minY)
case (let x, let y) where x < self.frame.origin.x + CornerTouchSize && y > self.frame.maxY - CornerTouchSize:
anchor = CGPoint(x: self.frame.maxX, y: self.frame.minY)
corner = CGPoint(x: self.frame.minX, y: self.frame.maxY)
case (let x, let y) where x > self.frame.maxX - CornerTouchSize && y < self.frame.origin.y + CornerTouchSize:
anchor = CGPoint(x: self.frame.minX, y: self.frame.maxY)
corner = CGPoint(x: self.frame.maxX, y: self.frame.minY)
case (let x, let y) where x > self.frame.maxX - CornerTouchSize && y > self.frame.maxY - CornerTouchSize:
anchor = CGPoint(x: self.frame.minX, y: self.frame.minY)
corner = CGPoint(x: self.frame.maxX, y: self.frame.maxY)
default:
self.moveFrame(self.frame, initialTouchLocation: location , nLocation)
}
if let anchor = anchor {
if let corner = corner {
self.didMove = true
self.isSelected = true
self.updateRect(anchor, initialTouchLocation: location, originalCorner: corner, nLocation)
self.updateLayers()
}
}
CATransaction.commit()
return true
}
var didMove = false
override func continueTracking(_ touch: UITouch, with event: UIEvent?) -> Bool {
CATransaction.begin()
CATransaction.setDisableActions(true)
didMove = true
let location = touch.location(in: self.superview)
nLocation = location
//self.trackingFrameTransform?(location)
self.updateLayers()
CATransaction.commit()
return true
}
override func endTracking(_ touch: UITouch?, with event: UIEvent?) {
CATransaction.begin()
CATransaction.setDisableActions(true)
if !didMove {
self.isSelected = !self.isSelected
}
didMove = false
self.updateLayers()
// self.trackingFrameTransform = nil
nLocation = nil
CATransaction.commit()
}
func updateCircleLayer(_ layer: CALayer, center: CGPoint) {
layer.isHidden = !self.isSelected
layer.frame = CGRect(x: center.x - circleRadius, y: center.y - circleRadius, width: 2 * circleRadius, height: 2 * circleRadius)
layer.backgroundColor = self.tintColor.cgColor
layer.borderColor = strokeTintColor
layer.cornerRadius = self.circleRadius
layer.borderWidth = 1
layer.setNeedsDisplay()
}
func inset() -> CGFloat {
let circleInset = (CornerTouchSize - (self.circleRadius * 2)) / 2
return self.circleRadius + circleInset
}
func updateBorderLayer() {
self.borderLayer.masksToBounds = false
self.borderLayer.borderWidth = 1
self.borderLayer.borderColor = self.tintColor.cgColor
let inset = self.inset()
self.borderLayer.frame = self.bounds.insetBy(dx: inset, dy: inset)
self.borderLayer.setNeedsDisplay()
}
}
DrawableView.swift
import Foundation
import UIKit
struct ColoredRect {
let color: UIColor
let origin: CGPoint
let size: CGSize
var width: CGFloat {
get {
return self.size.width
}
}
var height: CGFloat {
get {
return self.size.height
}
}
}
class DrawableView: UIControl {
fileprivate let colorPicker = ColorPicker()
fileprivate var currentRect: ResizableRectangleView?
fileprivate var originalLocation: CGPoint?
fileprivate var rectIsPending = false
var contentSize: CGSize?
var contentBounds: CGRect? {
get {
if let contentSize = self.contentSize {
let scale = min(self.bounds.width / contentSize.width, self.bounds.height / contentSize.height)
let scaledWidth = contentSize.width * scale
let scaledHeight = contentSize.height * scale
let x = round(0.5 * (self.bounds.width - scaledWidth))
let y = round(0.5 * (self.bounds.height - scaledHeight))
return CGRect(x: x, y: y, width: scaledWidth, height: scaledHeight)
} else {
return nil
}
}
}
var shapes: [ColoredRect] {
get {
var shapes = [ColoredRect]()
for view in self.subviews {
if let view = view as? ResizableRectangleView {
let f = view.convert(view.borderedFrame(), to: self)
let relX = min(1.0, max(0.0, f.origin.x / self.bounds.width))
let relY = min(1.0, max(0.0, f.origin.y / self.bounds.height))
let relWidth = min(1.0, max(0.0, f.width / self.bounds.width))
let relHeight = min(1.0, max(0.0, f.height / self.bounds.height))
let relOrigin = CGPoint(x: relX, y: relY)
let relSize = CGSize(width: relWidth, height: relHeight)
let rect = ColoredRect(color: view.tintColor, origin: relOrigin, size: relSize)
shapes.append(rect)
}
}
return shapes
}
set {
let shapes = newValue
for view in self.subviews {
if let view = view as? ResizableRectangleView {
view.removeFromSuperview()
}
}
self.colorPicker.alpha = 0
for shape in shapes {
let x = shape.origin.x * self.bounds.width
let y = shape.origin.y * self.bounds.height
let width = shape.width * self.bounds.width
let height = shape.height * self.bounds.height
let rectFrame = CGRect(x: x, y: y, width: width, height: height)
let view = ResizableRectangleView()
let inset = view.inset()
view.tintColor = shape.color
view.frame = rectFrame.insetBy(dx: -inset, dy: -inset)
view.delegate = self
self.addSubview(view)
}
self.bringSubview(toFront: self.colorPicker)
}
}
// override init() {
// super.init()
// self.addColorPicker()
// }
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.addColorPicker()
}
override init(frame: CGRect) {
super.init(frame: frame)
self.addColorPicker()
}
override func awakeFromNib() {
super.awakeFromNib()
self.addColorPicker()
}
fileprivate func addColorPicker() {
colorPicker.delegate = self
colorPicker.alpha = 0
self.addSubview(colorPicker)
self.bringSubview(toFront: self.colorPicker)
colorPicker.frame = CGRect(x: self.bounds.width - 44, y: 0, width: 44, height: self.bounds.height)
}
override func layoutSubviews() {
super.layoutSubviews()
colorPicker.frame = CGRect(x: self.bounds.width - 44, y: 0, width: 44, height: self.bounds.height)
}
override var canBecomeFirstResponder : Bool {
return true
}
override func motionEnded(_ motion: UIEventSubtype, with event: UIEvent?) {
if (motion == UIEventSubtype.motionShake) {
self.shapes = []
}
}
override func beginTracking(_ touch: UITouch, with event: UIEvent?) -> Bool {
let location = touch.location(in: self)
if let contentBounds = self.contentBounds {
if (!contentBounds.contains(location)) {
return false
}
}
rectIsPending = true
let newRect = ResizableRectangleView()
newRect.frame = CGRect(x: location.x, y: location.y, width: 1, height: 1)
newRect.tintColor = UIColor(cgColor: self.colorPicker.color)
self.currentRect = newRect
self.originalLocation = location
CATransaction.begin()
CATransaction.setDisableActions(true)
for view in self.subviews {
if let view = view as? ResizableRectangleView {
view.isSelected = false
view.updateLayers()
}
}
CATransaction.commit()
return true
}
override func continueTracking(_ touch: UITouch, with event: UIEvent?) -> Bool {
if let currentRect = self.currentRect {
if rectIsPending {
currentRect.delegate = self
self.addSubview(currentRect)
self.bringSubview(toFront: self.colorPicker)
}
CATransaction.begin()
CATransaction.setDisableActions(true)
if let originalLocation = self.originalLocation {
let location = touch.location(in: self)
currentRect.updateRect(originalLocation, initialTouchLocation: originalLocation, originalCorner: originalLocation, location)
// currentRect.updateRect(originalLocation, initialTouchLocation: originalLocation, originalCorner: originalLocation ,location: location)
}
CATransaction.commit()
}
return super.continueTracking(touch, with: event)
}
override func endTracking(_ touch: UITouch?, with event: UIEvent?) {
self.currentRect = nil
self.rectIsPending = false
}
}
extension DrawableView: ColorPickerDelegate {
func colorPicker(_ picker: ColorPicker, didChangeColor color: CGColor) {
CATransaction.begin()
CATransaction.setDisableActions(true)
for view in self.subviews {
if let view = view as? ResizableRectangleView {
if view.isSelected {
view.tintColor = UIColor(cgColor: color)
view.updateLayers()
}
}
}
CATransaction.commit()
}
}
extension DrawableView: ResizableRectangleViewDelegate {
func didSelectResizableRectangleView(_ view: ResizableRectangleView) {
self.bringSubview(toFront: self.colorPicker)
if self.colorPicker.alpha == 0 {
UIView.animate(withDuration: 0.15, animations: {
self.colorPicker.alpha = 1
})
}
}
func didDeselectResizableRectangleView(_ view: ResizableRectangleView) {
self.bringSubview(toFront: self.colorPicker)
if colorPicker.alpha == 1 {
let selectionCount = self.subviews.reduce(0) {
acc, view in
if let view = view as? ResizableRectangleView {
return acc + (view.isSelected ? 1 : 0)
}
return acc
}
if selectionCount == 0 {
UIView.animate(withDuration: 0.15, animations: {
self.colorPicker.alpha = 0
})
}
}
}
}