草庐IT

ios - SpriteKit - 安全区域布局 Iphone X

coder 2023-09-05 原文

所以,目前我有一个游戏,我用 sprite kit 制作并使用这种方式让所有东西都适合屏幕尺寸:

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)

如您所见,它使用框架来计算宽度和高度以及节点在场景中的位置,这适用于我从 6s 到 8 Plus 一直使用的所有屏幕尺寸。 但是当谈到 iPhone X 时,它就出现了问题。与 iPhone 8 Plus 相比,它似乎拉伸(stretch)了一切,因为屏幕尺寸更大且形状奇特,如下图所示

我一直在寻找这个问题的解决方案,但没有一个真正有帮助,甚至只是使它变得更糟,我无法理解如何以编程方式在我的 sprite 工具包项目中使用安全区域布局,就像在这个解决方案中一样 here

我的问题是

我如何才能让所有东西都适合 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 上尝试过它,例如下面的这个,它适用于我不知道为什么 yellow label 会这样做。

 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) 

结果是这样的,很完美:

最佳答案

有趣的问题。当我们试图将我们的 View “转换”为SKView 以创建我们的游戏场景时,问题就出现了。本质上,我们可以拦截那个时刻(使用 viewWillLayoutSubviews 而不是 viewDidLoad)并根据 safeAreaLayoutGuide 转换 View 框架。布局框架属性。

一些代码作为例子:

游戏 View Controller :

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

其他方式:

另一种只获取安全区域布局框架和当前 View 框架之间的差异而不通过 viewWillLayoutSubviews 来启动我们的场景的方法可能是使用协议(protocol)。

P.S.:在下方,我报告了一张引用图片,您可以在其中看到不同尺寸:

  • 主屏幕高度(我们的 View 高度)
  • 安全区
  • 状态栏高度(在顶部,iPhone X 为 44 像素)

游戏 View Controller :

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,如下例所示:

游戏 View Controller :

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

引用资料:

关于ios - SpriteKit - 安全区域布局 Iphone X,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48333872/

有关ios - SpriteKit - 安全区域布局 Iphone X的更多相关文章

  1. ruby - 如何使用 Ruby aws/s3 Gem 生成安全 URL 以从 s3 下载文件 - 2

    我正在编写一个小脚本来定位aws存储桶中的特定文件,并创建一个临时验证的url以发送给同事。(理想情况下,这将创建类似于在控制台上右键单击存储桶中的文件并复制链接地址的结果)。我研究过回形针,它似乎不符合这个标准,但我可能只是不知道它的全部功能。我尝试了以下方法:defauthenticated_url(file_name,bucket)AWS::S3::S3Object.url_for(file_name,bucket,:secure=>true,:expires=>20*60)end产生这种类型的结果:...-1.amazonaws.com/file_path/file.zip.A

  2. ruby - 如何验证 IO.copy_stream 是否成功 - 2

    这里有一个很好的答案解释了如何在Ruby中下载文件而不将其加载到内存中:https://stackoverflow.com/a/29743394/4852737require'open-uri'download=open('http://example.com/image.png')IO.copy_stream(download,'~/image.png')我如何验证下载文件的IO.copy_stream调用是否真的成功——这意味着下载的文件与我打算下载的文件完全相同,而不是下载一半的损坏文件?documentation说IO.copy_stream返回它复制的字节数,但是当我还没有下

  3. Ruby 文件 IO 定界符? - 2

    我正在尝试解析一个文本文件,该文件每行包含可变数量的单词和数字,如下所示:foo4.500bar3.001.33foobar如何读取由空格而不是换行符分隔的文件?有什么方法可以设置File("file.txt").foreach方法以使用空格而不是换行符作为分隔符? 最佳答案 接受的答案将slurp文件,这可能是大文本文件的问题。更好的解决方案是IO.foreach.它是惯用的,将按字符流式传输文件:File.foreach(filename,""){|string|putsstring}包含“thisisanexample”结果的

  4. ruby - nanoc 和多种布局 - 2

    是否可以为特定(或所有)项目使用多个布局?例如,我有几个项目,我想对其应用两种不同的布局。一个是绿色的,一个是蓝色的(但是)。我想将它们编译到我的输出目录中的两个不同文件夹中(例如v1和v2)。我一直在玩弄规则和编译block,但我不知道这是怎么回事。因为,每个项目在编译过程中只编译一次,我不能告诉nanoc第一次用layout1编译,第二次用layout2编译。我试过这样的东西,但它导致输出文件损坏。compile'*'doifitem.binary?#don’tfilterbinaryitemselsefilter:erblayout'layout1'layout'layout2'

  5. ruby - 如何安全地删除文件? - 2

    在Ruby中是否有Gem或安全删除文件的方法?我想避免系统上可能不存在的外部程序。“安全删除”指的是覆盖文件内容。 最佳答案 如果您使用的是*nix,一个很好的方法是使用exec/open3/open4调用shred:`shred-fxuz#{filename}`http://www.gnu.org/s/coreutils/manual/html_node/shred-invocation.html检查这个类似的帖子:Writingafileshredderinpythonorruby?

  6. Get https://registry-1.docker.io/v2/: net/http: request canceled while waiting - 2

    1.错误信息:Errorresponsefromdaemon:Gethttps://registry-1.docker.io/v2/:net/http:requestcanceledwhilewaitingforconnection(Client.Timeoutexceededwhileawaitingheaders)或者:Errorresponsefromdaemon:Gethttps://registry-1.docker.io/v2/:net/http:TLShandshaketimeout2.报错原因:docker使用的镜像网址默认为国外,下载容易超时,需要修改成国内镜像地址(首先阿里

  7. ruby - 用 YAML.load 解析 json 安全吗? - 2

    我正在使用ruby2.1.0我有一个json文件。例如:test.json{"item":[{"apple":1},{"banana":2}]}用YAML.load加载这个文件安全吗?YAML.load(File.read('test.json'))我正在尝试加载一个json或yaml格式的文件。 最佳答案 YAML可以加载JSONYAML.load('{"something":"test","other":4}')=>{"something"=>"test","other"=>4}JSON将无法加载YAML。JSON.load("

  8. ruby - 为什么不能使用类IO的实例方法noecho? - 2

    print"Enteryourpassword:"pass=STDIN.noecho(&:gets)puts"Yourpasswordis#{pass}!"输出:Enteryourpassword:input.rb:2:in`':undefinedmethod`noecho'for#>(NoMethodError) 最佳答案 一开始require'io/console'后来的Ruby1.9.3 关于ruby-为什么不能使用类IO的实例方法noecho?,我们在StackOverflow上

  9. ruby-on-rails - 安全地显示使用回形针 gem 上传的图像 - 2

    默认情况下:回形针gem将所有附件存储在公共(public)目录中。出于安全原因,我不想将附件存储在公共(public)目录中,所以我将它们保存在应用程序根目录的uploads目录中:classPost我没有指定url选项,因为我不希望每个图像附件都有一个url。如果指定了url:那么拥有该url的任何人都可以访问该图像。这是不安全的。在user#show页面中:我想实际显示图像。如果我使用所有回形针默认设置,那么我可以这样做,因为图像将在公共(public)目录中并且图像将具有一个url:Someimage:看来,如果我将图像附件保存在公共(public)目录之外并且不指定url(同

  10. ruby - 使写入文件线程安全 - 2

    我在一个ruby​​文件中有一个函数可以像这样写入一个文件File.open("myfile",'a'){|f|f.puts("#{sometext}")}这个函数在不同的线程中被调用,使得像上面这样的文件写入不是线程安全的。有谁知道如何以最简单的方式使这个文件写入线程安全?更多信息:如果重要的话,我正在使用rspec框架。 最佳答案 您可以通过File#flock给锁File.open("myfile",'a'){|f|f.flock(File::LOCK_EX)f.puts("#{sometext}")}

随机推荐