Hi. I have faced a problem in a seemingly easy situation. I am using SpriteKit to present several boxes (SKShapeNodes) with texts inside (similar to a messaging app). The boxes are created just nice. However, when I rotate the device, the lowest and the second to lowest boxes are placed as expected, then the next box is located higher (by the height of the box), the other one is placed even higher (I wish I could attach screenshots).
This is my code.
ViewController
class GameViewController: UIViewController {
override func viewDidLoad()
{
super.viewDidLoad()
let scene = GameScene()
let skView = self.view as! SKView
if skView.bounds.size.height>skView.bounds.size.width { scene.size = CGSize(width: 1080, height: 1920) }
else { scene.size = CGSize(width: 1920, height: 1080) }
skView.showsFPS = true
skView.showsNodeCount = true
skView.ignoresSiblingOrder = false
scene.scaleMode = .aspectFit
skView.presentScene(scene)
}
override func viewWillLayoutSubviews()
{
super.viewDidLayoutSubviews()
let skView = self.view as! SKView
if let scene = skView.scene
{
if skView.bounds.size.height>skView.bounds.size.width
{
scene.size = CGSize(width: 1080, height: 1920)
}
else
{
scene.size = CGSize(width: 1920, height: 1080)
}
var size = scene.size
let boundWidth = skView.bounds.width
let boundHeight = skView.bounds.height
let newHeight = boundHeight*size.width/boundWidth
let newWidth = boundWidth*size.height/boundHeight
if newHeight > size.height
{
size.height = newHeight
scene.size = size
}
if newWidth > size.width
{
size.width = newWidth
scene.size = size
}
}
}
}
GameScene:
import SpriteKit
import GameplayKit
struct TextMessage: Equatable
{
var messageText : SKLabelNode?
var messageBox : SKShapeNode?
}
class GameScene: SKScene {
var background : SKSpriteNode!
private var label : SKLabelNode?
private var spinnyNode : SKShapeNode?
var listOfTextMessages = [TextMessage]()
var number = 1
override func didMove(to view: SKView)
{
background = SKSpriteNode()
background.color = .green
addChild(background)
}
override func touchesEnded(_ touches: Set, with event: UIEvent?) {
switch number {
case 1:
createTextMessageBox(text:"wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww")
case 2:
createTextMessageBox(text:"\(number)\(number)\(number)")
case 3:
createTextMessageBox(text:"eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee")
case 4:
createTextMessageBox(text:"rrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr")
case 5:
createTextMessageBox(text:"\(number)\(number)\(number)")
default:
createTextMessageBox(text:"yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy")
}
number += 1
}
func createTextMessageBox(text:String)
{
// Width of the textbox is fixed and depends on the device position
var textboxWidth : CGFloat = 500
if UIDevice.current.orientation.isLandscape { textboxWidth = 800 }
else { textboxWidth = 500 }
// Create the text message label
let notificationText = SKLabelNode(fontNamed: "AppleSDGothicNeo-Regular")
notificationText.name = "message_text"
notificationText.text = text
notificationText.fontSize = 45
notificationText.fontColor = SKColor.white
notificationText.alpha = 0
notificationText.numberOfLines = 0
notificationText.horizontalAlignmentMode = .left
notificationText.verticalAlignmentMode = .center
notificationText.preferredMaxLayoutWidth = textboxWidth-50
// Height of the textbox depends on the size of the text
let textboxHeight = notificationText.frame.height + 50
// Notification textbox that contains the text
let rightEdgeX = self.convert(CGPoint(x: self.frame.maxX, y: 0), to: background).x
let bottomEdgeY = self.convert(CGPoint(x: 0, y: self.frame.minY), to: background).y
let boxPositionX = rightEdgeX-50-textboxWidth
let boxPositionY : CGFloat = bottomEdgeY+100
// Create the Notification Textbox
let notificationNode = SKShapeNode(rect: CGRect(x: boxPositionX,y: boxPositionY,width: textboxWidth,height: textboxHeight), cornerRadius: 20)
notificationNode.name = "message_box"
notificationNode.fillColor = UIColor(red: 120/255, green: 0/255, blue: 0/255, alpha: 0.8)
notificationNode.strokeColor = UIColor(red: 128/255, green: 0/255, blue: 0/255, alpha: 1)
notificationNode.lineWidth = 2
notificationNode.alpha = 0
//notificationNode.zPosition = 0
// Position text in the middle of the texbox
notificationText.position = CGPoint(x: notificationNode.frame.minX+25, y: notificationNode.frame.maxY-textboxHeight/2)
// Add nodes to the scene
background.addChild(notificationNode)
notificationNode.addChild(notificationText)
// Add to the list of text messages
let currentMessage = TextMessage(messageText: notificationText, messageBox: notificationNode)
listOfTextMessages.insert(currentMessage, at: 0)
// The first message is shown at the bottom, whereas the older messages are moved on top of it.
for (index,textBox) in listOfTextMessages.enumerated()
{
// The latest message
if index == 0
{
let actionBoxFadeIn = SKAction.fadeAlpha(to: 0.8, duration: 0.2) // Fade in the textbox
let actionTextFadeIn = SKAction.run { textBox.messageText!.run(SKAction.fadeIn(withDuration: 0.2)) }
let actionMoveGroup = SKAction.group([actionBoxFadeIn,actionTextFadeIn])
textBox.messageBox!.run(actionMoveGroup)
}
else
{
textBox.messageBox!.position.y += listOfTextMessages[0].messageBox!.frame.height
}
}
}
override func didChangeSize(_ oldSize: CGSize)
{
super.didChangeSize(oldSize)
var zPosition : CGFloat = 10
if background != nil
{
background.size = CGSize(width: self.frame.width, height: self.frame.height)
background.position = CGPoint(x: self.frame.width/2, y: self.frame.height/2)
}
if self.view != nil && !listOfTextMessages.isEmpty
{
let textboxWidth : CGFloat = UIDevice.current.orientation.isLandscape ? 800 : 500
let textlabelWidth = textboxWidth-50
for (index,textBox) in listOfTextMessages.enumerated()
{
textBox.messageBox!.zPosition = zPosition
zPosition += 1
textBox.messageText!.fontSize = 45
textBox.messageText!.fontColor = SKColor.white
textBox.messageText!.numberOfLines = 0
textBox.messageText!.horizontalAlignmentMode = .left
textBox.messageText!.verticalAlignmentMode = .center
textBox.messageText!.preferredMaxLayoutWidth = textlabelWidth
let textboxHeight = textBox.messageText!.frame.height + 50
let rightEdgeX = self.convert(CGPoint(x: self.frame.maxX, y: 0), to: background).x
let boxPositionX = rightEdgeX-50-textboxWidth
let boxPositionY : CGFloat = {
return (index == 0) ? self.convert(CGPoint(x: 0, y: self.frame.minY), to: background).y : listOfTextMessages[index-1].messageBox!.frame.minY
}()
textBox.messageBox!.path = UIBezierPath(roundedRect: CGRect(x: boxPositionX, y: boxPositionY, width: textboxWidth, height: textboxHeight), cornerRadius: 20).cgPath
textBox.messageText!.position = CGPoint(x: textBox.messageBox!.frame.minX+25, y: textBox.messageBox!.frame.minY+textboxHeight/2)
}
}
}
The text is also shifted upwards.
Interesting enough, if I do the following (create each box in the array using the coordinates of the first box), the boxes are placed right on top of each other; however, their position is also skewed when their height varies:
let boxPositionY : CGFloat = {
return self.convert(CGPoint(x: 0, y: self.frame.minY), to: background).y
}()
textBox.messageText!.position = CGPoint(x: listOfTextMessages[0].messageBox!.frame.minX+25, y: listOfTextMessages[0].messageBox!.frame.minY+textboxHeight/2)
Could someone please help me find the culprit?
Thanks a lot!