yyl
2026-05-11 51b0f6ed9f4e1d3bb6f8144470b46908c7699a96
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
using UnityEngine;
 
/// <summary>
/// 安全区域适配组件 - 自动适配刘海屏和异形屏
/// </summary>
[RequireComponent(typeof(RectTransform))]
[ExecuteInEditMode]
public class SafeAreaAdapter : MonoBehaviour
{
    [Header("适配设置")]
    [Tooltip("是否在编辑器模式下模拟安全区域")]
    public bool simulateInEditor = false;
    
    [Tooltip("适配边缘(Top, Bottom, Left, Right)")]
    public bool adaptTop = true;
    public bool adaptBottom = true;
    public bool adaptLeft = true;
    public bool adaptRight = true;
    
    [Header("调试信息")]
    [SerializeField] private Rect currentSafeArea;
    [SerializeField] private Vector2Int screenSize;
    
    private RectTransform _rectTransform;
    private Rect _lastSafeArea = Rect.zero;
    private Vector2Int _lastScreenSize = Vector2Int.zero;
    
    private void Awake()
    {
        _rectTransform = GetComponent<RectTransform>();
        ApplySafeArea();
    }
    
    private void Update()
    {
        // 检测屏幕变化或安全区域变化
        if (HasScreenChanged())
        {
            ApplySafeArea();
        }
    }
    
    /// <summary>
    /// 检测屏幕是否发生变化
    /// </summary>
    private bool HasScreenChanged()
    {
        Rect safeArea = GetSafeArea();
        Vector2Int screenSize = new Vector2Int(Screen.width, Screen.height);
        
        bool changed = safeArea != _lastSafeArea || screenSize != _lastScreenSize;
        
        _lastSafeArea = safeArea;
        _lastScreenSize = screenSize;
        
        return changed;
    }
    
    /// <summary>
    /// 获取安全区域
    /// </summary>
    private Rect GetSafeArea()
    {
        #if UNITY_EDITOR
        if (simulateInEditor)
        {
            // 编辑器模式下模拟 iPhone X 的安全区域
            float screenWidth = Screen.width;
            float screenHeight = Screen.height;
            
            // 模拟刘海(顶部 44px)和底部手势条(底部 34px)
            float topInset = 44f;
            float bottomInset = 34f;
            float leftInset = 0f;
            float rightInset = 0f;
            
            return new Rect(
                leftInset,
                bottomInset,
                screenWidth - leftInset - rightInset,
                screenHeight - topInset - bottomInset
            );
        }
        #endif
        
        return Screen.safeArea;
    }
    
    /// <summary>
    /// 应用安全区域适配
    /// </summary>
    private void ApplySafeArea()
    {
        if (_rectTransform == null)
            return;
        
        Rect safeArea = GetSafeArea();
        currentSafeArea = safeArea;
        screenSize = new Vector2Int(Screen.width, Screen.height);
        
        // 计算锚点位置
        Vector2 anchorMin = safeArea.position;
        Vector2 anchorMax = safeArea.position + safeArea.size;
        
        // 转换为标准化坐标 (0-1)
        anchorMin.x /= Screen.width;
        anchorMin.y /= Screen.height;
        anchorMax.x /= Screen.width;
        anchorMax.y /= Screen.height;
        
        // 根据设置决定是否适配各个边缘
        Vector2 finalAnchorMin = _rectTransform.anchorMin;
        Vector2 finalAnchorMax = _rectTransform.anchorMax;
        
        if (adaptLeft)
            finalAnchorMin.x = anchorMin.x;
        
        if (adaptBottom)
            finalAnchorMin.y = anchorMin.y;
        
        if (adaptRight)
            finalAnchorMax.x = anchorMax.x;
        
        if (adaptTop)
            finalAnchorMax.y = anchorMax.y;
        
        _rectTransform.anchorMin = finalAnchorMin;
        _rectTransform.anchorMax = finalAnchorMax;
        
        // 重置偏移
        _rectTransform.offsetMin = Vector2.zero;
        _rectTransform.offsetMax = Vector2.zero;
        
        Debug.Log($"[SafeAreaAdapter] 安全区域已应用: {safeArea}, 屏幕: {screenSize}");
    }
    
    /// <summary>
    /// 强制重新应用安全区域
    /// </summary>
    public void ForceApply()
    {
        ApplySafeArea();
    }
    
    #if UNITY_EDITOR
    private void OnValidate()
    {
        if (_rectTransform == null)
            _rectTransform = GetComponent<RectTransform>();
        
        ApplySafeArea();
    }
    #endif
}