有趣的问题。当我们试图“转换”我们的视图以SKView
创建我们的游戏场景时,问题就出现了。本质上,我们可以截取那一刻(使用viewWillLayoutSubviews
代替viewDidLoad
)并根据safeAreaLayoutGuide布局框架属性转换视图框架。
一些代码作为例子:
游戏视图控制器:
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("---")
}
}
游戏场景:
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:在下面,我报告了一张参考图片,您可以在其中看到不同的尺寸:
- 主屏幕高度(我们的视图高度)
- 安全区
- 状态栏高度(在顶部,iPhone X 为 44 像素)
游戏视图控制器:
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)
}
}
游戏场景:
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
如下示例:
游戏视图控制器:
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()
}
}
}
游戏场景:
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
}
}
}
参考: