SpriteKit - безопасная схема расположения Iphone X
Итак, в настоящее время у меня есть игра, которую я сделал в наборе спрайтов, и я использовал этот способ подгонки всего к размеру экрана:
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)
как вы можете видеть, он использует рамку для вычисления ширины и высоты вместе с положением узла на сцене, это работает со всеми размерами экрана, которые я использовал от 6 до 8 плюс. но когда дело доходит до iPhone X есть проблема с ним. кажется, что все растягивается из-за того, что размер экрана больше и странной формы, как вы можете видеть ниже по сравнению с iPhone 8 Plus
Я искал решения этой проблемы, но ни один из них не помог, а просто не усугубил ситуацию, и я не мог понять, как программно использовать макет безопасной области в моем проекте набора спрайтов, как в этом решении здесь
Мой вопрос
Как мне сделать так, чтобы все помещалось на экране iPhone X так, чтобы оно умещалось и не отсекало метки партитуры и прочее, что у меня есть в верхнем правом углу?
РЕДАКТИРОВАТЬ 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)
РЕДАКТИРОВАТЬ 3
РЕДАКТИРОВАТЬ 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)
РЕДАКТИРОВАТЬ 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)
Я попробовал это на другом 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)
Получилось вот так, и это идеально:
2 ответа
Вы можете получить поля через ваш вид safeAreaInsets
, Вставки будут нулевыми на большинстве устройств, но ненулевыми на iPhone X.
Например, замените вашу строку на это:
let safeAreaInsets = yourSpriteKitView.safeAreaInsets;
buyButton.position = CGPoint(x:safeAreaInsets.left - frame.maxX + frame.midX*2, y: safeAreaInsets.top - frame.maxY + frame.midY*1.655)
Интересный вопрос. Проблема возникает, когда мы пытаемся "преобразовать" нашу точку зрения в SKView
создать нашу игровую сцену. По сути, мы могли бы перехватить этот момент (используя viewWillLayoutSubviews
вместо viewDidLoad
) и преобразуйте фрейм представления в соответствии со свойством фрейма макета safeAreaLayoutGuide.
Некоторый код в качестве примера:
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:
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)
}
}
Другой способ сделать:
Другой способ получить только различия между рамкой макета безопасной области и текущей рамкой просмотра, не проходя через viewWillLayoutSubviews
для запуска нашей сцены, можно использовать протоколы.
PS: внизу я привожу справочное изображение, где вы можете увидеть различные размеры между:
- высота основного экрана (высота нашего обзора)
- безопасный район
- высота строки состояния (сверху 44 пикселя для iPhone X)
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:
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)
}
}
Выход:
Методы разделения на подклассы:
Другой способ получить правильные размеры безопасной области без прикосновения к текущему коду классов, может быть сделан с использованием некоторого подкласса, поэтому для нашего GameViewController
мы устанавливаем это как GameController
и для GameScene
мы можем установить это как GenericScene
как этот пример:
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:
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
}
}
}