Avatar ·

Как определить размер экрана iOS устройства в Swift?

Всем привет! Я новичок в iOS разработке на Swift и столкнулся с проблемой при попытке получить размер экрана устройства. Мне нужно адаптировать интерфейс под разные размеры экранов (iPhone SE, iPhone 12, iPad и т.д.), но я не могу понять, как правильно это сделать. Я пробовал несколько способов, но они либо возвращают неправильные значения, либо вообще не работают. Вот что я пытался: ```swift let screenSize = UIScreen.main.bounds.size print("Screen size: \(screenSize)") ``` Но этот код вроде работает, но я читал, что `UIScreen.main` может быть устаревшим или ненадежным в некоторых случаях. Также я слышал, что нужно учитывать safe area и navigation bar, но не понимаю, как это влияет на размер экрана. Еще я пробовал использовать `UIApplication.shared.keyWindow?.bounds`, но это иногда возвращает nil. Подскажите, пожалуйста, какой самый надежный и современный способ получить размер экрана iOS устройства в Swift? Нужно ли учитывать ориентацию устройства? Как правильно получать размеры для разных компонентов интерфейса (например, для размещения элементов в ScrollView или для расчета размеров UIImageView)? Заранее спасибо за помощь! <<>>

Современный способ получения размера экрана в Swift

Основная проблема, с которой вы столкнулись — это использование устаревших API и непонимание контекста, в котором нужен размер экрана. Давайте разберем правильные подходы.

1. Для получения физического размера экрана устройства (в точках):

import UIKit

let screenBounds = UIScreen.main.bounds
print("Ширина: \(screenBounds.width), Высота: \(screenBounds.height)")

UIScreen.main.bounds возвращает размер экрана в точках (points), а не в пикселях. Это важно, так как на разных устройствах разная плотность пикселей (например, @2x на iPhone 8, @3x на iPhone 12 Pro). Этот метод не устарел, но его нужно использовать с осторожностью: он не учитывает ориентацию устройства (если вы не подписались на уведомления об изменении ориентации).

2. Для получения размера с учетом текущей ориентации:

// Внутри UIViewController
override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
    let currentBounds = view.bounds
    print("Размер вью с учетом ориентации: \(currentBounds.size)")
}

Лучший способ — использовать view.bounds внутри контроллера. Это автоматически обновляется при повороте устройства и учитывает safe area (если вы используете safeAreaInsets).

3. Для получения размера с учетом Safe Area:

// Внутри UIViewController
override func viewSafeAreaInsetsDidChange() {
    super.viewSafeAreaInsetsDidChange()
    let safeAreaBounds = view.bounds.inset(by: view.safeAreaInsets)
    print("Доступная область после safe area: \(safeAreaBounds.size)")
}

4. Для ScrollView и UICollectionView:

// Внутри viewDidLoad
let scrollView = UIScrollView(frame: view.bounds)
scrollView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
scrollView.contentSize = CGSize(width: view.bounds.width, height: 2000)

Никогда не задавайте жесткие размеры для ScrollView. Используйте view.bounds или UIScreen.main.bounds только для начальной настройки, а затем обновляйте через Auto Layout.

5. Определение типа устройства (iPhone/iPad):

if UIDevice.current.userInterfaceIdiom == .phone {
    print("Это iPhone")
} else if UIDevice.current.userInterfaceIdiom == .pad {
    print("Это iPad")
}

6. Получение размера в пикселях (для графики):

let screenScale = UIScreen.main.scale
let pixelWidth = screenBounds.width * screenScale
let pixelHeight = screenBounds.height * screenScale
print("Размер в пикселях: \(pixelWidth) x \(pixelHeight)")

Рекомендации

  • Не используйте UIApplication.shared.keyWindow — это deprecated с iOS 13. Используйте UIWindowScene или view.window.
  • Для адаптивной верстки всегда используйте Auto Layout и Size Classes вместо жестких расчетов размеров.
  • Для получения размеров вью дождитесь загрузки (viewDidLayoutSubviews или viewWillAppear).
  • Избегайте использования UIScreen.main в многоконных приложениях (iPadOS) — используйте view.window?.windowScene?.screen.

Пример полного решения для современного iOS приложения:

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        // Начальная настройка
        setupUI()
    }
    
    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        // Обновление размеров после изменения ориентации
        updateLayoutForCurrentSize()
    }
    
    private func setupUI() {
        let label = UILabel()
        label.text = "Размер экрана: \(UIScreen.main.bounds.size)"
        label.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(label)
        
        NSLayoutConstraint.activate([
            label.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            label.centerYAnchor.constraint(equalTo: view.centerYAnchor)
        ])
    }
    
    private func updateLayoutForCurrentSize() {
        print("Текущий размер вью: \(view.bounds.size)")
        print("Safe area insets: \(view.safeAreaInsets)")
    }
}

Этот подход гарантирует, что вы получите корректные размеры в любой ситуации, включая многозадачность на iPad и Dynamic Island на iPhone 14 Pro.

<<>>

Альтернативный подход: использование UIScreen.main.nativeBounds

Если вам нужен точный физический размер экрана в пикселях (а не в точках), используйте nativeBounds. Это особенно полезно для работы с камерой, Metal или Core Graphics.

let nativeBounds = UIScreen.main.nativeBounds
print("Физический размер в пикселях: \(nativeBounds.size)")

// Соотношение сторон
let aspectRatio = nativeBounds.width / nativeBounds.height
print("Соотношение сторон: \(aspectRatio)")

Важно: nativeBounds всегда возвращает размер в портретной ориентации, даже если устройство повернуто. Для получения текущей ориентации используйте UIScreen.main.bounds или view.bounds.

Определение конкретной модели устройства

Иногда проще определить модель по размеру экрана, чем адаптировать верстку:

enum DeviceModel {
    case iPhoneSE, iPhoneStandard, iPhonePlus, iPhoneProMax, iPad
    
    static var current: DeviceModel {
        let height = max(UIScreen.main.bounds.width, UIScreen.main.bounds.height)
        switch height {
        case 568: return .iPhoneSE       // iPhone SE (1st gen)
        case 667: return .iPhoneStandard // iPhone 6,7,8
        case 736: return .iPhonePlus     // iPhone 6+,7+,8+
        case 812: return .iPhoneProMax   // iPhone X, XS, 11 Pro
        case 896: return .iPhoneProMax   // iPhone XR, 11, 12
        default: return .iPad
        }
    }
}

print("Текущая модель: \(DeviceModel.current)")

Этот подход менее гибкий, чем Auto Layout, но может быть полезен для быстрых прототипов или специфических настроек (например, для разных разрешений изображений).