简体   繁体   English

SpriteKit-安全区域布局Iphone X

[英]SpriteKit - safe area layout Iphone X

So, Currently I have a game that i've made in sprite kit and have used this way of fitting everything to the screen size: 因此,目前,我有一个使用精灵套件制作的游戏,并使用这种方式将所有内容调整到屏幕尺寸:

buyButton = SKSpriteNode(texture: SKTexture(imageNamed: "BuyButton"), color: .clear, size: CGSize(width: frame.maxX / 2.9, height: frame.maxY / 10))
buyButton.position = CGPoint(x:-frame.maxX + frame.midX*2, y: -frame.maxY + frame.midY*1.655)
addChild(buyButton)

as you can see it uses the frame to calculate the width and height along with the position of the node on the scene, this works with all screen sizes that I have been using from the 6s to the 8 Plus. 如您所见,它使用框架来计算宽度和高度以及节点在场景中的位置,这适用于从6s到8 Plus的所有屏幕尺寸。 but when it comes to the iPhone X there is a problem with it. 但是当谈到iPhone X时,就出现了问题。 it seems to stretch everything because of the screen size being bigger and oddly shaped as you can see below compared to the iPhone 8 Plus 它似乎可以拉伸所有东西,因为与iPhone 8 Plus相比,屏幕尺寸更大且形状奇特,如下图所示

I've been looking for solutions to this problem but none have really helped or just even make it worse and I couldn't understand how to programmatically use the safe area layout in my sprite kit project like in this solution here 我一直在寻找这个问题的解决方案,但没有一个真正的帮助,或只是甚至更糟糕的是,我无法理解如何通过编程在该解决方案使用安全区域布局在我的精灵套件的项目像这里

My Question is 我的问题是

How do I go about getting everything to fit in the iPhone X's screen so that it fits and isn't cutting off the score labels and stuff I have in the top right corners? 我该如何使所有内容都适合iPhone X的屏幕,以使其适合并且不会切断得分标签和我右上角的内容?

EDIT 2: 编辑2:

itemDescriptionLabel = UILabel(frame: CGRect(x: frame.maxX - 150, y: frame.maxY - 130 , width: frame.maxX / 0.7, height: frame.maxY / 3.5))
itemDescriptionLabel.font = UIFont(name: "SFCompactRounded-Light", size: 17)
itemDescriptionLabel.text = "This purchase stops all the popup ads that happen after a certain amount of time playing the game from showing up."
itemDescriptionLabel.numberOfLines = 4
itemDescriptionLabel.adjustsFontSizeToFitWidth = true
self.scene?.view?.addSubview(itemDescriptionLabel)

EDIT 3 编辑3

在此处输入图片说明

EDIT 4 编辑4

let safeAreaInsets = yourSpriteKitView.safeAreaInsets;
buyButton.position = CGPoint(x:safeAreaInsets.left - frame.maxX + frame.midX*2, y: safeAreaInsets.top - frame.maxY + frame.midY*1.655)

EDIT 5 编辑5

screenWidth = self.view!.bounds.width
screenHeight = self.view!.bounds.height

coinScoreLabel = Score(num: 0,color: UIColor(red:1.0, green: 1.0, blue: 0.0, alpha: 1), size: 50, useFont: "SFCompactRounded-Heavy" )  // UIColor(red:1.00, green:0.81, blue:0.07, alpha:1.0)
coinScoreLabel.name = "coinScoreLabel"
coinScoreLabel.horizontalAlignmentMode = .right
//coinScoreLabel.verticalAlignmentMode = .top
coinScoreLabel.position = CGPoint(x: screenWidth / 2.02, y: inset.top - screenHeight / 2.02)
coinScoreLabel.zPosition = 750
addChild(coinScoreLabel)

I tried it on another SpriteNode that I have such as this one below, which it worked for I have no idea why the yellow label is doing this. 我在另一个SpriteNode上尝试过,例如下面这样,它对我有用,但我不知道为什么黄色标签会这样做。

 playButton = SKSpriteNode(texture: SKTexture(imageNamed: "GameOverMenuPlayButton"), color: .clear, size: CGSize(width: 120, height: 65))
 playButton.position = CGPoint(x: inset.right - frame.midX, y: inset.bottom + frame.minY + playButton.size.height / 1.7)
 playButton.zPosition = 1000
 deathMenuNode.addChild(playButton) 

It came out like this which is perfect: 像这样完美的出来了:

在此处输入图片说明

Interesting question. 有趣的问题。 The problem borns when we try to "transform" our view to SKView to create our game scene. 当我们尝试将视图 “转换”为SKView来创建游戏场景时, SKView就产生了。 Essentially, we could intercept that moment (using viewWillLayoutSubviews instead of viewDidLoad ) and transform the view frame accordling with the safeAreaLayoutGuide layout frame property. 本质上,我们可以拦截这一刻(使用viewWillLayoutSubviews而不是viewDidLoad ),并根据safeAreaLayoutGuide布局框架属性转换视图框架。

Some code as example: 一些代码作为示例:

GameViewController: GameViewController:

class GameViewController: UIViewController {
    override func viewWillLayoutSubviews() {
        super.viewWillLayoutSubviews()
        if #available(iOS 11.0, *), let view = self.view {
           view.frame = self.view.safeAreaLayoutGuide.layoutFrame
        }  
        guard let view = self.view as! SKView? else { return }
        view.ignoresSiblingOrder = true
        view.showsFPS = true
        view.showsNodeCount = true
        view.showsPhysics = true
        view.showsDrawCount = true
        let scene = GameScene(size:view.bounds.size)
        scene.scaleMode = .aspectFill
        view.presentScene(scene)
    }
    override func viewDidLoad() {
        super.viewDidLoad()
        print("---")
        print("∙ \(type(of: self))")
        print("---")
    }
}

GameScene: GameScene:

class GameScene: SKScene {
    override func didMove(to view: SKView) {
        print("---")
        print("∙ \(type(of: self))")
        print("---")
        let labLeftTop = SKLabelNode.init(text: "LEFTTOP")
        labLeftTop.horizontalAlignmentMode = .left
        labLeftTop.verticalAlignmentMode = .top
        let labRightTop = SKLabelNode.init(text: "RIGHTTOP")
        labRightTop.horizontalAlignmentMode = .right
        labRightTop.verticalAlignmentMode = .top
        let labLeftBottom = SKLabelNode.init(text: "LEFTBTM")
        labLeftBottom.horizontalAlignmentMode = .left
        labLeftBottom.verticalAlignmentMode = .bottom
        let labRightBottom = SKLabelNode.init(text: "RIGHTBTM")
        labRightBottom.horizontalAlignmentMode = .right
        labRightBottom.verticalAlignmentMode = .bottom
        self.addChild(labLeftTop)
        self.addChild(labRightTop)
        self.addChild(labLeftBottom)
        self.addChild(labRightBottom)
        labLeftTop.position = CGPoint(x:0,y:self.frame.height)
        labRightTop.position = CGPoint(x:self.frame.width,y:self.frame.height)
        labLeftBottom.position = CGPoint(x:0,y:0)
        labRightBottom.position = CGPoint(x:self.frame.width,y:0)
    }
}

Other way to do: 其他方式:

Another way to obtain just only the differences between the safe area layout frame and the current view frame without passing through viewWillLayoutSubviews for launching our scene, could be using protocols. 另一种仅获取安全区域布局框架与当前视图框架之间的差异而无需通过viewWillLayoutSubviews来启动场景的方法是使用协议。

PS : Down below, I've report a reference image where you can see the different sizes between: PS :下面,我报告了一个参考图像,您可以在其中看到不同的尺寸:

  • the main screen height (our view height) 主屏幕高度(我们的视图高度)
  • safe area 安全区
  • status bar height (on the top, 44 px for the iPhone X) 状态栏高度(顶部,iPhone X为44像素)

GameViewController: GameViewController:

protocol LayoutSubviewDelegate: class {
    func safeAreaUpdated()
}
class GameViewController: UIViewController {
    weak var layoutSubviewDelegate:LayoutSubviewDelegate?
    override func viewWillLayoutSubviews() {
        super.viewWillLayoutSubviews()
        if let _ = self.view {
           layoutSubviewDelegate?.safeAreaUpdated()
        }
    }
    override func viewDidLoad() {
        super.viewDidLoad()
        print("---")
        print("∙ \(type(of: self))")
        print("---")
        guard let view = self.view as! SKView? else { return }
        view.ignoresSiblingOrder = true
        view.showsFPS = true
        view.showsNodeCount = true
        view.showsPhysics = true
        view.showsDrawCount = true
        let scene = GameScene(size:view.bounds.size)
        scene.scaleMode = .aspectFill
        view.presentScene(scene)
    }
}

GameScene: GameScene:

class GameScene: SKScene,LayoutSubviewDelegate {
    var labLeftTop:SKLabelNode!
    var labRightTop:SKLabelNode!
    var labLeftBottom:SKLabelNode!
    var labRightBottom:SKLabelNode!
    func safeAreaUpdated() {
        if let view = self.view {
            let top = view.bounds.height-(view.bounds.height-view.safeAreaLayoutGuide.layoutFrame.height)+UIApplication.shared.statusBarFrame.height
            let bottom = view.bounds.height - view.safeAreaLayoutGuide.layoutFrame.height-UIApplication.shared.statusBarFrame.height
            refreshPositions(top: top, bottom: bottom)
        }
    }
    override func didMove(to view: SKView) {
        print("---")
        print("∙ \(type(of: self))")
        print("---")
        if let view = self.view, let controller = view.next, controller is GameViewController {
            (controller as! GameViewController).layoutSubviewDelegate = self
        }
        labLeftTop = SKLabelNode.init(text: "LEFTTOP")
        labLeftTop.horizontalAlignmentMode = .left
        labLeftTop.verticalAlignmentMode = .top
        labRightTop = SKLabelNode.init(text: "RIGHTTOP")
        labRightTop.horizontalAlignmentMode = .right
        labRightTop.verticalAlignmentMode = .top
        labLeftBottom = SKLabelNode.init(text: "LEFTBTM")
        labLeftBottom.horizontalAlignmentMode = .left
        labLeftBottom.verticalAlignmentMode = .bottom
        labRightBottom = SKLabelNode.init(text: "RIGHTBTM")
        labRightBottom.horizontalAlignmentMode = .right
        labRightBottom.verticalAlignmentMode = .bottom
        self.addChild(labLeftTop)
        self.addChild(labRightTop)
        self.addChild(labLeftBottom)
        self.addChild(labRightBottom)
        labLeftTop.position = CGPoint(x:0,y:self.frame.height)
        labRightTop.position = CGPoint(x:self.frame.width,y:self.frame.height)
        labLeftBottom.position = CGPoint(x:0,y:0)
        labRightBottom.position = CGPoint(x:self.frame.width,y:0)
    }
    func refreshPositions(top:CGFloat,bottom:CGFloat){
        labLeftTop.position = CGPoint(x:0,y:top)
        labRightTop.position = CGPoint(x:self.frame.width,y:top)
        labLeftBottom.position = CGPoint(x:0,y:bottom)
        labRightBottom.position = CGPoint(x:self.frame.width,y:bottom)
    }
}

Output: 输出:

在此处输入图片说明

Subclassing methods: 子类化方法:

Another way to get the correct safe area dimensions without touch your current classes code, could be made using some subclass, so for our GameViewController we set it as GameController and for GameScene we can set it as GenericScene like this example: 另一种方式来获得无触摸你的当前类代码正确的安全区域的尺寸,可以使用一些子类进行,因此对于我们的GameViewController我们将它作为GameControllerGameScene我们可以将其设置为GenericScene这样的例子:

GameViewController: GameViewController:

class GameViewController: GameController {
    override func viewDidLoad() {
        super.viewDidLoad()
        guard let view = self.view as! SKView? else { return }
        view.ignoresSiblingOrder = true
        let scene = GameScene(size:view.bounds.size)
        scene.scaleMode = .aspectFill
        view.presentScene(scene)
    }
}
protocol LayoutSubviewDelegate: class {
    func safeAreaUpdated()
}
class GameController:UIViewController {
    weak var layoutSubviewDelegate:LayoutSubviewDelegate?
    override func viewWillLayoutSubviews() {
        super.viewWillLayoutSubviews()
        if let _ = self.view {
            layoutSubviewDelegate?.safeAreaUpdated()
        }
    }
}

GameScene: GameScene:

class GameScene: GenericScene {
    override func safeAreaUpdated() {
        super.safeAreaUpdated()
        if let view = self.view {
            let insets = view.safeAreaInsets
            // launch your code to update positions here
        }
    }
    override func didMove(to view: SKView) {
        super.didMove(to: view)
        // your stuff
    }
}
class GenericScene: SKScene, LayoutSubviewDelegate {
    func safeAreaUpdated(){}
    override func didMove(to view: SKView) {
        if let view = self.view, let controller = view.next, controller is GameViewController {
            (controller as! GameViewController).layoutSubviewDelegate = self
        }
    }
}

References: 参考文献:

在此处输入图片说明

You can get margins through your view's safeAreaInsets . 您可以通过视图的safeAreaInsets获得边距。 The insets will be zero on most devices, but non-zero on the iPhone X. 在大多数设备上,插图将为零,但在iPhone X上,插图将为非零。

For example replace your line with this: 例如,用以下代码替换您的行:

let safeAreaInsets = yourSpriteKitView.safeAreaInsets;
buyButton.position = CGPoint(x:safeAreaInsets.left - frame.maxX + frame.midX*2, y: safeAreaInsets.top - frame.maxY + frame.midY*1.655)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM