当前位置: 首页 > news >正文

适配iPhoneX、iPhoneXs、iPhoneXs Max、iPhoneXr 屏幕尺寸及安全区域

此篇文章是对上一篇文章(http://www.ifiero.com/index.p...)的进一步补充,主要说明如何适配Apple的最新三款手机iPhoneXs、iPhoneXs Max及iPhoneXr !!!

未适配前:Ball球超过屏幕的上下方

适配后:Ball球就在屏幕的可视范围内运动了

回顾:为何要把场景中的所有图片, 都按照屏幕大小为 2048 1536 来绘制。 也就是说, 我们的背景图的大小是 2048 1536, 其他图片也是依照这个比例来绘制。

为什么这样做呢?

我们知道 2048 1536 是iPad Retina 的分辨率。也是我们需要适配的设备里面分辨率最高的。 所以我们在游戏中都选择了这个大小,让它来兼容分辨率低的设备。 2048 1536 在iPad Retina上是完美显示的。 那在其他设备上呢? 先用 AspectFill来进行缩放,并应用相应的屏幕辨率高宽比值Ratio, 来适配各个不同的iPhone尺寸。AspectFill缩放的代码如下:

if let scene = GameScene(fileNamed: "GameScene") {
                scene.size = CGSize(width: 2048, height: 1536)
                scene.scaleMode = .aspectFill /// 缩放
                view.presentScene(scene)
 }
了解了用 AspectFill来进行缩放,那么我们现在就来看看iPhoneX的屏幕尺寸分辨率

以iPhoneX的分辨率为例,SafeArea为安全区域

iPhoneX的屏幕尺寸分辨率(上图中 高度812 = 2436缩小1/3):

设备屏幕尺寸分辨率图片存放对应的位置
iPhoneX (1倍 @1x)375x812@1x
iPhoneX (2倍 @2x)750x1624@2x
iPhoneX (3倍 @3x)1125x2436@3x
iPhoneX/iPhoneXs/iPhoneXs Max/iPhoneXr的屏幕尺寸分辨率

iPhoneX系列的分辨率

iPhoneX系列的屏幕分辨率:

设备屏幕分辨率图片存放的位置
iPhoneX (3倍 @3x)1125x2436@3x
iPhoneXs (3倍 @3x)1125x2436@3x
iPhoneXs Max (3倍 @3x)1242x2688@3x
iPhoneXr (2倍 @2x)828x1792@2x
根据以上iPhoneX系列的屏幕分辨率,得出高宽比Ratio都为2.16

各款iPhone的屏幕分辨比率

橙色整体区域表示我们场景的真实大小, 用 AspectFill来进行缩放后,scene.scaleMode = .aspectFill,黑色线框内的区域表示场景展示在设备上的真实大小(即屏幕可视范围)。

iPad Retina:橙色区域和黑色线框内的区域是完美吻合的,也就是说在设备上能完整显示。

iPhone6/7/8/Plus:黑色线框内的区域是2048 * 1152,这边要注意的是,超出黑色框的内容看不见,设计游戏时,尽量不要把精灵的Position位置放在位于不可见的区域。

iPhoneX:黑色线框内的区域是2048 * 948(兰色为安全区域),其中948高度=2048 / 2.16(高宽比)。

不同尺寸的iPhone的屏幕尺寸比例及屏幕高宽比值
| 设备 | 屏幕比例 | 屏幕高宽比值 |
| - | :-: | -: |
| iPad Retina | 4 / 3 | 1.33 |
| iPhone 6/7/8 | 16 / 9 | 1.77 |
| iPhone 6/7/8 Plus | 16 / 9 | 1.77 |
| iPhone X/Xs/Xr/Xs Max | -- | 2.16 |

再者我们主要是适配SpriteKit游戏开发,因此不需要计算Navigation导航栏的高度,也不需要TabBar状态栏的高度,只需要计算可视区域(屏幕可视范围),还有,注意要把需要交互的元素放在安全区域SafeArea,而不要放在危险区域 Danger Area就行了。

可视区域(屏幕可视范围)= 安全区域 Safe Area + 危险区域 Danger Area

了解了原理后,我们就开始来编写代码吧。

1.extension拓展UIDevice,判断设备是iPhone或者iPhoneX系列或iPad

import UIKit
import SpriteKit

// iPhone X  375*812(H) @1x
// 竖屏
public let AREA_INSET_HEIGHT_TOP   :CGFloat = (UIScreen.main.bounds.height == 812 || UIScreen.main.bounds.height == 896) ? 44.0 : 0
public let AREA_INSET_HEIGHT_BOTTOM:CGFloat = (UIScreen.main.bounds.height == 812 || UIScreen.main.bounds.height == 896) ? 34.0 : 0
// 横屏(安全区域)
public let AREA_INSET_WIDTH_LEFT  :CGFloat = (UIScreen.main.bounds.width == 812 || UIScreen.main.bounds.width == 896) ? 44.0 : 0
public let AREA_INSET_WIDTH_RIGHT :CGFloat = (UIScreen.main.bounds.width == 812 || UIScreen.main.bounds.width == 896) ? 34.0 : 0

extension UIDevice {
    /// 是不是iPhoneX ,如果是竖屏则 UIScreen.main.bounds.height == 812
    public func isPhoneX() -> Bool {
        if UIScreen.main.bounds.width == 812 || UIScreen.main.bounds.width == 896 {  /// 横屏
            return true
        }
        return false
    }
    /// 是不是iPad
    public func isPad() -> Bool {
        return (UIDevice.current.userInterfaceIdiom == .pad) ? true : false;
    }
    
}

2.检测是哪种设备

  // MARK: - 检测是哪种设备
    func initCheckDevice(){
        if UIDevice.current.isPhoneX() {
            maxAspectRatio = 2.16         /// iPhoneX || iPhoneXs || iPhoneXs Max || iPhoneXr 2.16 高/宽比 ratio
        }else {
            maxAspectRatio  = UIDevice.current.isPad() ? (4.0 / 3.0) : (16.0 / 9.0)  /// iPhone 16:9,iPad 4:3
        }
        /// 画出可视区域
        drawPlayableArea(size: self.size,ratio: maxAspectRatio)
        /// 画出安全区域
        drawSafeArea(size: self.size,ratio: maxAspectRatio)
    }

3.画出安全区域

 // MARK: - 安全区域即用户交互的区域,非可视区域 (iPhoneX的安全区域 < 可视区域)
    func drawSafeArea(size:CGSize,ratio:CGFloat){
        
        playableHeight  = size.width / ratio
        playableMargin = (size.height - playableHeight ) / 2.0   /// P70
        
        let safeInsetLeft   =  AREA_INSET_WIDTH_LEFT * ratio
        let safeInsetRight  =  size.width - safeInsetLeft - AREA_INSET_WIDTH_RIGHT * ratio
        
        playableRect = CGRect(x: safeInsetLeft, y: playableMargin, width:safeInsetRight, height:  playableHeight)  /// 注意 scene的anchorPoint(0,0)原点的位置;
        let shapeFrame = SKShapeNode(rect: playableRect)
        shapeFrame.zPosition = 2
        shapeFrame.strokeColor = SKColor.green
        shapeFrame.lineWidth = 6.0
        addChild(shapeFrame)
        
    }

4.画出可视区域并赋于可视区域的边届物理特性

 // MARK: - 画出可视区域
    func drawPlayableArea(size:CGSize,ratio:CGFloat){
        
        playableHeight  = size.width / ratio
        playableMargin = (size.height - playableHeight ) / 2.0   /// P70
        playableRect = CGRect(x: 0, y: playableMargin, width: size.width, height:  playableHeight)  /// 注意 scene的anchorPoint(0,0)原点的位置;
        print("playable Margin",playableMargin)
        
        let shapeFrame = SKShapeNode(rect: playableRect)
        shapeFrame.zPosition = 1
        shapeFrame.strokeColor = SKColor.red
        shapeFrame.lineWidth = 5.0
        addChild(shapeFrame)
        
        /// 可视区域的物理状态
        let playableBody = SKPhysicsBody(edgeLoopFrom: playableRect)
        playableBody.friction = 0
        self.physicsBody = playableBody
        playableBody.categoryBitMask    = PhysicsCategory.Frame
        playableBody.contactTestBitMask = PhysicsCategory.Ball
        playableBody.collisionBitMask   = PhysicsCategory.Ball
        
        /// 地板
        setupFloor()
    }

这样子Ball球就只在可视区域内(屏幕可视范围)运动了。
适配iPhoneXs Max

重要的一点就是要了解屏幕尺寸和安全区域的不同,通俗点讲就是,屏幕可视范围可以放任何元素,但所有的用户交互行为都要放在安全区域内(兰色框内)。

即可视区域(屏幕可视范围)= 安全区域 Safe Area + 危险区域 Danger Area

源码传送门: https://github.com/apiapia/Br...
更多游戏教程: http://www.iFIERO.com -- 为游戏开发深感自豪

相关文章:

  • MySQL是如何做容器测试的?
  • C# 异或校验算法
  • gitbook.explore更新升级了, 不能再搜索了
  • 通过flat.vmdk恢复esxi虚拟机
  • MFS分布式存储部署
  • 使用shell脚本自动安装Apache2.4.35
  • JAVA 同步实现原理
  • Java Agent 学习笔记
  • mysql如何直接查出从1开始递增的数
  • GlassFish新纪元
  • 基于树莓派的桌上足球计分器
  • C# 高级编程03----细节内容
  • mongodb之 oplog 日志详解
  • 动态库空间优化
  • 贝叶斯分类器
  • 【翻译】Mashape是如何管理15000个API和微服务的(三)
  • Centos6.8 使用rpm安装mysql5.7
  • ECMAScript6(0):ES6简明参考手册
  • JavaScript设计模式系列一:工厂模式
  • java概述
  • JS笔记四:作用域、变量(函数)提升
  • log4j2输出到kafka
  • 模型微调
  • ​iOS安全加固方法及实现
  • ​软考-高级-系统架构设计师教程(清华第2版)【第1章-绪论-思维导图】​
  • #Lua:Lua调用C++生成的DLL库
  • (2022 CVPR) Unbiased Teacher v2
  • (8)Linux使用C语言读取proc/stat等cpu使用数据
  • (ctrl.obj) : error LNK2038: 检测到“RuntimeLibrary”的不匹配项: 值“MDd_DynamicDebug”不匹配值“
  • (DFS + 剪枝)【洛谷P1731】 [NOI1999] 生日蛋糕
  • (Spark3.2.0)Spark SQL 初探: 使用大数据分析2000万KF数据
  • (蓝桥杯每日一题)平方末尾及补充(常用的字符串函数功能)
  • (七)c52学习之旅-中断
  • (三分钟了解debug)SLAM研究方向-Debug总结
  • (一)搭建springboot+vue前后端分离项目--前端vue搭建
  • (转载)深入super,看Python如何解决钻石继承难题
  • .gitignore文件_Git:.gitignore
  • .NET 设计模式—简单工厂(Simple Factory Pattern)
  • .netcore 如何获取系统中所有session_如何把百度推广中获取的线索(基木鱼,电话,百度商桥等)同步到企业微信或者企业CRM等企业营销系统中...
  • .net快速开发框架源码分享
  • @font-face 用字体画图标
  • [C++数据结构](22)哈希表与unordered_set,unordered_map实现
  • [CareerCup] 2.1 Remove Duplicates from Unsorted List 移除无序链表中的重复项
  • [CLR via C#]11. 事件
  • [CSDN首发]鱿鱼游戏的具体玩法详细介绍
  • [HackMyVM]靶场 Wild
  • [IE编程] 如何设置IE8的WebBrowser控件(MSHTML) 的渲染模式
  • [LeetCode]--61. Rotate List
  • [MQ]常用的mq产品图形管理web界面或客户端
  • [NAND Flash 6.1] 怎么看时序图 | 从时序理解嵌入式 NAND Read 源码实现
  • [NOIP2014] 提高组 洛谷P1941 飞扬的小鸟
  • [ORM]register db Ping `default`, Error 1130: Host '' is not allow connect to this MySQL server
  • [Python]面向对象基础
  • [SAP] 解决程序提示被某用户使用或锁定的问题
  • [Share]17个免费下载电子书的网站