-1

I am using SpriteKit to present several boxes (SKShapeNodes) with texts inside, like this:

enter image description here

Then I rotate my device anticipating that the boxes will retain their original position no matter what the orientation is, but get this instead:

enter image description here

Can someone help me find the culprit?

This is my code:

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<UITouch>, with event: UIEvent?) {
        createTextMessageBox(text:"\(number)\(number)\(number)")
        number += 1
    }

    func createTextMessageBox(text:String)
    {
        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 = 1

        // 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) 
                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)

        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
        {
            if UIDevice.current.orientation.isLandscape
            {
                let textboxWidth : CGFloat = 800
                let textlabelWidth = textboxWidth-50

                for (index,textBox) in listOfTextMessages.enumerated()
                {
                    if index == 0
                    {
                        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 bottomEdgeY = self.convert(CGPoint(x: 0, y: self.frame.minY), to: background).y
                        let boxPositionX = rightEdgeX-50-textboxWidth
                        let boxPositionY : CGFloat = bottomEdgeY+100

                        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.maxY-textboxHeight/2)
                    }
                    else
                    {
                        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 = listOfTextMessages[index-1].messageBox!.frame.minY //+ textboxHeight


                        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)
                    }
                }
            }
            else
            {
                let textboxWidth : CGFloat = 500
                let textlabelWidth = textboxWidth-50

                for (index,textBox) in listOfTextMessages.enumerated()
                {
                    if index == 0
                    {
                        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 bottomEdgeY = self.convert(CGPoint(x: 0, y: self.frame.minY), to: background).y
                        let boxPositionX = rightEdgeX-50-textboxWidth
                        let boxPositionY : CGFloat = bottomEdgeY+100


                        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.maxY-textboxHeight/2)
                    }
                    else
                    {
                        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 = listOfTextMessages[index-1].messageBox!.frame.maxY - textboxHeight


                        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)
                    }
                }
            }
        }
    }
}

Thank you!

EDIT:

Code for the ViewController:

import UIKit
import SpriteKit
import GameplayKit

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

               // Bound size of the SKView in Points
               let boundWidth = skView.bounds.width
               let boundHeight = skView.bounds.height

               // New Height and Width ratio based on points
               let newHeight = boundHeight*size.width/boundWidth
               let newWidth = boundWidth*size.height/boundHeight

               // Adjust the new height in the scene
               if newHeight > size.height
               {
                   //scene.anchorPoint = CGPoint(x: 0, y: (newHeight - scene.size.height) / 2.0 / newHeight)
                   size.height = newHeight
                   scene.size = size
               }

               // Adjust the new width in the scene
               if newWidth > size.width
               {
                   //scene.anchorPoint = CGPoint(x: 0, y: (newWidth - scene.size.width) / 2.0 / newWidth)
                   size.width = newWidth
                   scene.size = size
               }
           }
       }
}
Igor Tupitsyn
  • 1,193
  • 3
  • 18
  • 45

1 Answers1

1

Here is a clean up of your change size code. The only difference I found was when setting boxPositionY

    override func didChangeSize(_ oldSize: CGSize)
    {
        super.didChangeSize(oldSize)

        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.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 = {
                    if UIDevice.current.orientation.isLandscape
                    {
                        return (index == 0) ? self.convert(CGPoint(x: 0, y: self.frame.minY), to: background).y+100 : listOfTextMessages[index-1].messageBox!.frame.minY //+ textboxHeight
                    }
                    else
                    {
                        return (index == 0) ? self.convert(CGPoint(x: 0, y: self.frame.minY), to: background).y+100 : listOfTextMessages[index-1].messageBox!.frame.maxY - textboxHeight
                    }
                }()
                /////////////////////////////////////////

                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)
            }


        }
    }
Knight0fDragon
  • 16,609
  • 2
  • 23
  • 44
  • nice cleanup. I believe your missing parentheses after your closure. it errors without them – Ron Myschuk Jan 09 '20 at 16:12
  • Thank you, Ron. Still no luck. Result is almost the same, except that the text for the fourth box ("444") is below the lowest box. I have added code for my ViewController in case it may affect. Thank you for your support though. – Igor Tupitsyn Jan 10 '20 at 00:02
  • This isnt a fix.... this is for you to check the difference in y – Knight0fDragon Jan 10 '20 at 00:23
  • Thanks. Y positions are identical for both codes: `Y__index: 0; boxPositionY: -859.9999389648438 Y__index: 1; boxPositionY: -858.1499786376953 Y__index: 2; boxPositionY: -745.5999908447266 Y__index: 3; boxPositionY: -522.3499908447266 Y__index: 0; boxPositionY: -860.4799194335938 Y__index: 1; boxPositionY: -858.6300201416016 Y__index: 2; boxPositionY: -746.0800323486328 Y__index: 3; boxPositionY: -522.8300323486328` – Igor Tupitsyn Jan 10 '20 at 00:30
  • `didChangeSize` is called twice, hence two sets of printed values. – Igor Tupitsyn Jan 10 '20 at 00:30
  • This code I posted is your code, cleaned up. The y area that is still in the if branch surrounded by comments is where your error may be – Knight0fDragon Jan 10 '20 at 00:31
  • Hm. Interesting. Replacing `listOfTextMessages[index-1]` with `listOfTextMessages[0]` fixes placement of shape nodes, although the text position is still skewed. But I do not see any logic in this whatsoever! – Igor Tupitsyn Jan 10 '20 at 01:54
  • That shouldnt fix it, I am more curious as to why 1 is max and 1 is min. It should be exactly the same – Knight0fDragon Jan 10 '20 at 01:55
  • Huh? This is how your code is now. Your box position Y is using minY for landscape and maxY for portrait.... this may be causing your problem because this should be the same. This if statement should not exist. So figure out which one of those two you should use, and make it consistent with the other – Knight0fDragon Jan 10 '20 at 02:16
  • I am using maxY in both cases now. This has no effect. If I place every component in the array using the same Y position, boxes are placed above each other. This is not a solution, but I think this is what causes the problem. – Igor Tupitsyn Jan 10 '20 at 02:25
  • I am done, I can’t help you out anymore I am sorry. This is a really easy comparison I provides for you to check and you are missing the mark. You are not suppose to set everything to the same Y position, you just need to decide which one of the 2 return statements is correct, and use that math formula for both orientations. The if statement will then go away, and you would be left with let boxPositionY : CGFloat = (index == 0) ...... the rest of the formula you picked – Knight0fDragon Jan 10 '20 at 02:31
  • None of them are correct. This is the point. You would understand if you ran the code. Thanks for your time anyways. – Igor Tupitsyn Jan 10 '20 at 02:36