获取屏幕宽度

在 SwiftUI 和 UIKit 开发中,有多种方式可以用来获取屏幕的宽度。不同的场景和需求可能需要选择最合适的方法。以下是几种常见的方法:


1. 使用 UIScreen 来获取屏幕宽度 #

UIScreen.main.bounds 是最直接的方法,用于获取设备的屏幕尺寸(包括宽度和高度)。这是 基于 UIKit 的方式

示例代码 #

import SwiftUI

let screenWidth = UIScreen.main.bounds.width
print("屏幕宽度是: \(screenWidth)")

解释 #

  • UIScreen.main.bounds.width 返回当前设备屏幕的宽度,单位是 点(points)(而非像素,点值会根据设备的屏幕比例 scale 映射到实际像素值)。
  • 注意: 如果应用支持分屏(on iPad),返回的 bounds 可能会是屏幕分割后的宽度。

2. 使用 GeometryReader(推荐在 SwiftUI 中使用) #

在 SwiftUI 中,GeometryReader 是一个强大的工具,可以获取视图的尺寸和位置。而且它在自适应布局中更为灵活。

示例代码 #

import SwiftUI

struct ContentView: View {
    var body: some View {
        GeometryReader { geometry in
            VStack {
                Text("屏幕宽度: \(geometry.size.width)")
                    .padding()
                Text("屏幕高度: \(geometry.size.height)")
            }
        }
        .background(Color.yellow)
    }
}

解释 #

  • geometry.size.width 获取当前屏幕的宽度。
  • 优势GeometryReader 会动态适应父容器的大小,因此可以获取基于当前布局上下文的宽度(甚至可以应对旋转或分屏场景)。
  • 注意:如果嵌套在布局中,GeometryReader 获取的可能是父视图的宽度,而非整个屏幕宽度。

3. 使用 UIScreenwindowScene 结合获取当前窗口宽度 #

如果要获取 当前窗口当前场景 的宽度(尤其适用于多场景、多窗口的 iPad 应用),可以通过 UIKit 的 UIWindowScene 获取窗口大小。

示例代码 #

import SwiftUI

func getScreenWidth() -> CGFloat {
    guard let window = UIApplication.shared.connectedScenes
            .first(where: { $0.activationState == .foregroundActive }) as? UIWindowScene else {
        return UIScreen.main.bounds.width // 回退到默认屏幕宽度
    }
    return window.screen.bounds.width
}

let screenWidth = getScreenWidth()
print("当前窗口宽度是: \(screenWidth)")

解释 #

  • 如果你的应用中有多个场景(SceneDelegate),window.screen.bounds.width 可以返回当前场景所占用的宽度。
  • 更适合多窗口、分屏场景的应用。

4. 响应式布局:获取实际显示视图的宽度(View 宽度) #

除了基本的屏幕宽度,很多时候你可能希望获取 视图本身在屏幕中的实际宽度

示例代码 #

import SwiftUI

struct ResponsiveWidthExample: View {
    @State private var viewWidth: CGFloat = 0
    
    var body: some View {
        VStack {
            Text("视图宽度: \(viewWidth)")
                .padding()
            Rectangle()
                .foregroundColor(.blue)
                .frame(height: 100)
                .background(
                    GeometryReader { geometry in
                        Color.clear
                            .onAppear {
                                self.viewWidth = geometry.size.width // 获取子视图的宽度
                            }
                    }
                )
        }
        .frame(maxWidth: .infinity) // 让 VStack 填充到屏幕宽度
    }
}

解释 #

  • GeometryReader 获取的是 父视图的宽度,但通过 onAppear 事件,可以提取任意视图的宽度。
  • 适用场景:如果你需要知道一个嵌套布局(比如子视图)的实际宽度,而不是整个屏幕宽度。

**5. 使用 Environment 提供的 horizontalSizeClass #

SwiftUI 提供了响应式布局的工具,比如 Environment 中的 horizontalSizeClass,可以检测当前屏幕的大小分类(compact 或 regular),从而间接调整视图布局逻辑。

示例代码 #

import SwiftUI

struct EnvironmentExample: View {
    @Environment(\.horizontalSizeClass) var sizeClass

    var body: some View {
        VStack {
            if sizeClass == .compact {
                Text("屏幕宽度为紧凑类型 (Compact)")
            } else {
                Text("屏幕宽度为常规类型 (Regular)")
            }
        }
        .frame(maxWidth: .infinity)
    }
}

解释 #

  • sizeClass 的值:
    • Compact(窄屏宽度,例如 iPhone 竖屏、一小侧 iPad 分屏模式等)
    • Regular(宽屏宽度,例如 iPad、iPhone 横屏或全屏模式)
  • 适用于响应式布局,而非直接使用宽度值。

6. 使用 Combine/观察屏幕宽度变化(高级进阶) #

在复杂场景下,你可能需要实时监听屏幕尺寸变化(比如设备旋转、分屏等)。以下是基于 NotificationCenter 和 Combine 的监听方法。

示例代码 #

import SwiftUI
import Combine

class ScreenSizeObserver: ObservableObject {
    @Published var screenWidth: CGFloat = UIScreen.main.bounds.width
    private var cancellables = Set<AnyCancellable>()

    init() {
        NotificationCenter.default.publisher(for: UIDevice.orientationDidChangeNotification)
            .sink { [weak self] _ in
                self?.screenWidth = UIScreen.main.bounds.width
            }
            .store(in: &cancellables)
    }
}

配合视图:

struct ScreenSizeListenerExample: View {
    @StateObject private var observer = ScreenSizeObserver()

    var body: some View {
        Text("当前屏幕宽度: \(observer.screenWidth)")
            .padding()
    }
}

解释 #

  • 使用 Combine(或其他事件流机制)监听设备方向、分屏事件。
  • NotificationCenter 会派发 UIDevice.orientationDidChangeNotification,可用来更新屏幕宽度。

总结 #

获取方式适用场景
UIScreen.main.bounds.width最常见也是最简单的方式,适合获取设备屏幕的原始宽度。适用于全屏显示布局。
GeometryReaderSwiftUI 下获取视图宽度的推荐方式。本地适配性强,支持动态布局和分屏。
UIScreen + Scene适合高级场景(如 iPad 的分屏、多窗口应用)获取当前活动窗口的宽度。
子视图宽度获取与动态更新如果需要获取具体视图的宽度,配合 GeometryReaderonAppear,可以动态计算宽度(包括子视图)。
Environment(\.horizontalSizeClass)响应式布局场景,利用系统提供的尺寸分类(紧凑或常规)调整 UI 布局,而不直接获取宽度值。
Combine 监听需要实时更新屏幕宽度变化,适用于设备方向、分屏模式监听等复杂场景。

选择具体方式时:

  • 如果你需要一般的屏幕宽度值,UIScreen.main.bounds.width 是最简单直接的。
  • 如果基于 SwiftUI 且想动态适配布局,GeometryReader 是首选。
  • 如果需要考虑分屏或响应屏幕变化,结合 UIScreenScene 或 Combine 是更好的选择。
本文共 1820 字,上次修改于 Jan 10, 2025