UIDevice에 대해 다뤄보도록 하겠습니다.
UIDevice
UIDevice 클래스에 접근하면 해당 디바이스의 정보를 알 수 있습니다.
디바이스의 정보를 가지고 어떤방향으로 개발을 해야될지 알아 보겠습니다.
- 디바이스 이름 / 모델 / 화면 방향 등
- OS 이름 / 버전
- 인터페이스 형식 (phone, pad, tv 등)
- 배터리 정보
- 근접 센서 정보
- 멀티태스킹 지원 여부
접근 방법
let device = UIDevice.current
system버전 보기
예를 들어 현재 iPhone의 버전이 11.4.2라고 했을때 11은 Major, 4는 Minor(public), 2는 Minor(non-public)와 같이 Major, Minor (public), Minor (non-public)가 존재합니다.
또, 앱 개발을 할때 버전별로 API가 다를수 있기때문에 분기처리를 위해 systemVersion을 알아야합니다.
if #available(iOS 9.0, *) {
print("9.0이상")
} else {
print("9.0미만")
}
systemVersion이라는 IBAction을 만들어서 확인해보겠습니다.
@IBAction private func systemVersion() {
print("\n---------- [ System Version ] ----------\n")
print("System Name :", device.systemName)
print((device.systemVersion as NSString).floatValue) // ex) 11.4라고 했을때 11은 메이저 버전, 4는 마이너 버전
let systemVersion = device.systemVersion
print(systemVersion)
//좀 더 세부적으로 기능을 나누고 싶을때 메이저, 마이너(public), 마이너(non-public)으로 나눠서 개발 할 수 있다.
let splitVersion = systemVersion.split(separator: ".").compactMap { Int($0) }
print(splitVersion)
if splitVersion.count > 2 {
label.text = "\(splitVersion[0]).\(splitVersion[1]).\(splitVersion[2])"
} else {
label.text = "\(splitVersion[0]).\(splitVersion[1])"
}
}
출력화면
---------- [ System Version ] ----------
System Name : iOS
11.4
11.4
[11, 4]
아키텍처
시뮬레이터와 Device의 정보를 얻을 수 있습니다.
@IBAction private func architecture() {
print("\n---------- [ Architecture ] ----------\n")
//#if (arch(i386) || arch(x86_64)) && os(iOS) // 시뮬레이터 구분 방법 1
#if targetEnvironment(simulator) // 시뮬레이터 구분 방법 2
print("Simulator")
label.text = "Simulator"
#else // Device
print("Device")
label.text = "Device"
#endif
// 1 1 0 1 1 1 1 0 0 0 Simulator
// 1 1 0 0 0 1 1 0 0 1 Device: iPhoneX
print("TARGET_OS_MAC : ", TARGET_OS_MAC)
print("TARGET_OS_IOS : ", TARGET_OS_IOS)
print("TARGET_CPU_X86 : ", TARGET_CPU_X86)
print("TARGET_CPU_X86_64 : ", TARGET_CPU_X86_64)
print("TARGET_OS_SIMULATOR : ", TARGET_OS_SIMULATOR)
print("TARGET_RT_64_BIT : ", TARGET_RT_64_BIT)
print("TARGET_RT_LITTLE_ENDIAN :", TARGET_RT_LITTLE_ENDIAN)
print("TARGET_RT_BIG_ENDIAN :", TARGET_RT_BIG_ENDIAN)
print("TARGET_CPU_ARM : ", TARGET_CPU_ARM)
print("TARGET_CPU_ARM64 : ", TARGET_CPU_ARM64)
}
출력화면
1 1 0 1 1 1 1 0 0 0 Simulator
1 1 0 0 0 1 1 0 0 1 Device: iPhoneX
TARGET_OS_MAC : 1
TARGET_OS_IOS : 1
TARGET_CPU_X86 : 0
TARGET_CPU_X86_64 : 0
TARGET_OS_SIMULATOR : 0
TARGET_RT_64_BIT : 1
TARGET_RT_LITTLE_ENDIAN : 1
TARGET_RT_BIG_ENDIAN : 0
TARGET_CPU_ARM : 0
TARGET_CPU_ARM64 : 1
디바이스 모델
@IBAction private func deviceModel() {
print("\n---------- [ Device Model ] ----------\n")
print("name :", device.name)
print("model :", device.model)
print("localizedModel :", device.localizedModel)
print("userInterfaceIdiom :", device.userInterfaceIdiom)
print("orientation :", device.orientation)
/***************************************
<UIDeviceOrientation>
case unknown
case portrait // Device oriented vertically, home button on the bottom
case portraitUpsideDown // Device oriented vertically, home button on the top
case landscapeLeft // Device oriented horizontally, home button on the right
case landscapeRight // Device oriented horizontally, home button on the left
case faceUp // 기기가 위로 놓여져 있을때
case faceDown // 기기가 뒤짚어 있을때
****************************************/
print("isMultitaskingSupported :", device.isMultitaskingSupported) // 최신 기기에서는 항상 true
}
출력화면
---------- [ Device Model ] ----------
name : 김승진의 iPhone
model : iPhone
localizedModel : iPhone
userInterfaceIdiom : phone
orientation : UIDeviceOrientation
위의 출력 결과를 보면 iPhone에 대한 인지는하고 있지만 iPhone의 종류가 무엇인지 세세하게 나와있지 않습니다.
보통 정확한 정보를 위해 Custom하여 Device에 대한 정보를 표현해냅니다.
extension UIDevice {
// 상세한 모델을 표현하기 위한 로직
var modelIdentifier: String {
var sysinfo = utsname()
uname(&sysinfo)
let data = Data(bytes: &sysinfo.machine, count: Int(_SYS_NAMELEN))
let identifier = String(bytes: data, encoding: .ascii)!
return identifier.trimmingCharacters(in: .controlCharacters)
}
// 모델 프로퍼티
var modelName: String {
return deviceModelMappingList[modelIdentifier] ?? "Unknown"
}
// 디바이스 모델에 대한 상세정보를 저장해 놓습니다.
// 세세한 정보에 대해 분기처리를 할 경우 이런식으로 모델에 대한정보를 뽑아 올 수 있습니다.
private var deviceModelMappingList: [String: String] {
return [
"iPhone1,1" : "iPhone", // (Original)
"iPhone1,2" : "iPhone", // (3G)
"iPhone2,1" : "iPhone", // (3GS)
"iPhone3,1" : "iPhone 4", // (GSM)
"iPhone3,3" : "iPhone 4", // (CDMA)
"iPhone4,1" : "iPhone 4S", //
"iPhone5,1" : "iPhone 5", // (A1428)
"iPhone5,2" : "iPhone 5", // (A1429)
"iPhone5,3" : "iPhone 5c", // (A1456/A1532)
"iPhone5,4" : "iPhone 5c", // (A1507/A1516/A1529)
"iPhone6,1" : "iPhone 5s", // (A1433, A1453)
"iPhone6,2" : "iPhone 5s", // (A1457, A1518, A1530)
"iPhone7,1" : "iPhone 6 Plus", //
"iPhone7,2" : "iPhone 6", //
"iPhone8,1" : "iPhone 6S", //
"iPhone8,2" : "iPhone 6S Plus", //
"iPhone8,4" : "iPhone SE", //
"iPhone9,1" : "iPhone 7", // (A1660/A1779/A1780)
"iPhone9,3" : "iPhone 7", // (A1778)
"iPhone9,2" : "iPhone 7 Plus", // (A1661/A1785/A1786)
"iPhone9,4" : "iPhone 7 Plus", // (A1784)
"iPhone10,1": "iPhone 8", // (A1863/A1906) CDMA
"iPhone10,4": "iPhone 8", // (A1905) GSM
"iPhone10,2": "iPhone 8 Plus", // (A1864/A1898) CDMA
"iPhone10,5": "iPhone 8 Plus", // (A1897) GSM
"iPhone10,3": "iPhone X", // (A1865/A1902) CDMA
"iPhone10,6": "iPhone X", // (A1901) GSM
"iPad1,1" : "iPad", // (Original)
"iPad2,1" : "iPad 2", // (Wi-Fi)
"iPad2,2" : "iPad 2", // (GSM)
"iPad2,3" : "iPad 2", // (CDMA)
"iPad2,4" : "iPad 2", // (Wi-Fi, revised)
"iPad2,5" : "iPad Mini", // (Wi-Fi)
"iPad2,6" : "iPad Mini", // (A1454)
"iPad2,7" : "iPad Mini", // (A1455)
"iPad3,1" : "iPad 3", // (3rd Generation, Wi-Fi)
"iPad3,2" : "iPad 3", // (3rd Generation, Wi-Fi+LTE Verizon)
"iPad3,3" : "iPad 3", // (3rd Generation, Wi-Fi+LTE AT&T)
"iPad3,4" : "iPad 4", // (4th Generation, Wi-Fi)
"iPad3,5" : "iPad 4", // (4th Generation, A1459)
"iPad3,6" : "iPad 4", // (4th Generation, A1460)
"iPad4,1" : "iPad Air", // (Wifi)
"iPad4,2" : "iPad Air", // (Wi-Fi+LTE)
"iPad4,3" : "iPad Air", // (Rev)
"iPad4,4" : "iPad Mini 2", // (2nd Generation, Wifi)
"iPad4,5" : "iPad Mini 2", // (2nd Generation, Wi-Fi+LTE)
"iPad4,6" : "iPad Mini 2", // (2nd Generation, Rev)
"iPad4,7" : "iPad Mini 3", // (3rd Generation, Wifi, A1599)
"iPad4,8" : "iPad Mini 3", // (3rd Generation, A1600)
"iPad4,9" : "iPad Mini 3", // (3rd Generation, A1601)
"iPad5,1" : "iPad Mini 4", // (Wi-Fi)
"iPad5,2" : "iPad Mini 4", // (Wi-Fi+LTE)
"iPad5,3" : "iPad Air 2", // (Wi-Fi)
"iPad5,4" : "iPad Air 2", // (Wi-Fi+LTE)
"iPad6,3" : "iPad Pro (9.7 inch)", // (Wi-Fi)
"iPad6,4" : "iPad Pro (9.7 inch)", // (Wi-Fi+LTE)
"iPad6,7" : "iPad Pro (12.9 inch)", // (Wi-Fi)
"iPad6,8" : "iPad Pro (12.9 inch)", // (Wi-Fi+LTE)
"iPad6,11" : "iPad (9.7 inch)", // 5th Gen (Wi-Fi)
"iPad6,12" : "iPad (9.7 inch)", // 5th Gen (Wi-Fi+LTE)
"iPad7,3" : "iPad Pro (10.5 inch)", // (A1701)
"iPad7,4" : "iPad Pro (10.5 inch)", // (A1709)
"iPod1,1" : "iPod Touch", // (Original)
"iPod2,1" : "iPod Touch 2", // (2nd Generation)
"iPod3,1" : "iPod Touch 3", // (3rd Generation)
"iPod4,1" : "iPod Touch 4", // (4th Generation)
"iPod5,1" : "iPod Touch 5", // (5th Generation)
"iPod7,1" : "iPod Touch 6", // (6th Generation)
"i386" : "Simulator", // 32 bit
"x86_64" : "Simulator", // 64 bit
]
}
}
//extension
print("modelIdentifier :", device.modelIdentifier)
print("modelName :", device.modelName)
출력화면
---------- [ Device Model ] ----------
name : 김승진의 iPhone
model : iPhone
localizedModel : iPhone
userInterfaceIdiom : phone
orientation : UIDeviceOrientation
modelIdentifier : iPhone10,6
modelName : iPhone X
배터리 정보
배터리 상태에는 unknown, unplugged, charging, full 상태가 존재합니다.
IDeviceBatteryState : Int {
case unknown
case unplugged // on battery, discharging
case charging // plugged in, less than 100%
case full // plugged in, at 100%
}
배터리의 정보를 확인 하는 버튼을 만들었습니다.
@IBAction private func battery() {
print("\n-------------------- [ Battery Info ] --------------------\n")
label.text = "\(device.batteryState) : \(device.batteryLevel)"
print("batteryState :", device.batteryState)
print("batteryLevel :", device.batteryLevel)
print("isBatteryMonitoringEnabled :", device.isBatteryMonitoringEnabled)
}
위에 코드대로 바로 실행해보면 아래와 같이 출력화면을 볼 수 있습니다.
-------------------- [ Battery Info ] --------------------
batteryState : unknown
batteryLevel : -1.0
isBatteryMonitoringEnabled : false
기본적으로 isBatteryMonitoringEnabled는 false로 설정되어있기 때문입니다.
그래서 배터리 정보를 모니터링 할 수있는 활성화버튼을 하나 만들었습니다.
@IBAction private func batteryMonitoring(_ sender: UIButton) {
print("\n---------- [ batteryMonitoring ] ----------\n")
sender.isSelected = !sender.isSelected // 버튼을 눌렀을시 토글기능
//isBtteryMonitoringEnabled의 활성화 상태를 토글
//버튼 선택시 true로 바꾸기위한 작업.
//true로 바꿔야 정보를 가져 올 수 있다.
device.isBtteryMonitoringEnabled = !device.isBatteryMonitoringEnabled
//Notification의 UIDeviceBatteryStateDidChange의 상태로 정보를 가져온다.
if device.isBatteryMonitoringEnabled {
NotificationCenter.default.addObserver(
forName: .UIDeviceBatteryStateDidChange, object: nil, queue: .main
) {
if let device = $0.object as? UIDevice {
print("batteryState :", device.batteryState)
print("batteryLevel :", device.batteryLevel)
}
}
} else {
NotificationCenter.default.removeObserver(self, name: .UIDeviceBatteryStateDidChange, object: nil)
}
}
isBtteryMonitoringEnabled를 true의 상태로 바꿔주고 NotificationCenter로 UIDeviceBatteryStateDidChange을 감지하여 배터리의 정보를 가져오게끔 완성했습니다.
다시한번 battery() 버튼을 선택해보겠습니다.
출력화면
-------------------- [ Battery Info ] --------------------
batteryState : full
batteryLevel : 1.0
isBatteryMonitoringEnabled : true
근접 센서 정보
근접센서 proximity를 다뤄보겠습니다.
근접센서를 다루기 위해서는 먼저 활성화를 시켜줘야합니다.
device.isProximityMonitoringEnabled = true
활성화를 시켜줬다면 device기기에서 통화음이 들리는 곳에 위치한 근접센서에 손을 일정시간(0.5초?..)유지를 하고 있으면 화면이 꺼지는걸 볼 수 있을겁니다!
즉, 근접센서에 손이 가까이가면 true상태, 근접센서에 접근하는것이 없다면 false상태로 계속해서 유동적으로 변하게됩니다.
이렇게 상태가 변하는걸 보기위해서는 배터리의 상태정보를 확인하는거와 마찬가지로 Notification으로 상태확인이 가능합니다.
NotificationCenter.default.addObserver(
forName: .UIDeviceProximityStateDidChange, object: nil, queue: .main
) { [weak self] _ in
print(UIDevice.current.proximityState)
self?.label.text = "\(UIDevice.current.proximityState)"
}
label의 값이 true, false로 상태가 계속적으로 변하는것을 볼 수 있습니다.
@IBAction private func proximityMonitoring(_ sender: UIButton) {
// True가 어느정도 유지되는순간 화면이 꺼진다.
print("\n-------------------- [ Proximity Sensor ] --------------------\n")
sender.isSelected = !sender.isSelected
device.isProximityMonitoringEnabled = !device.isProximityMonitoringEnabled
print("ProximityMonitoring :", device.isProximityMonitoringEnabled)
if device.isProximityMonitoringEnabled {
//NotificationCenter에 UIDeviceProximityStateDidChange의 상태로 근접센서를 다룰 수 있습니다.
NotificationCenter.default.addObserver(
forName: .UIDeviceProximityStateDidChange, object: nil, queue: .main
) { [weak self] _ in
print(UIDevice.current.proximityState)
self?.label.text = "\(UIDevice.current.proximityState)"
}
} else {
NotificationCenter.default.removeObserver(self, name: .UIDeviceProximityStateDidChange, object: nil)
}
}
출력화면
-------------------- [ Proximity Sensor ] --------------------
ProximityMonitoring : true
true
false
true
false
화면 방향
UIDeviceOrientation(화면방향)의 상태는 unknown, portrait,portraitUpsideDown, landscapeLeft, landscapeRight, faceUp, faceDown가 있습니다.
직접 디바이스의 기기로 확인하기전에 제어화면에서 기기화면 고정옵션을 켜두었는지 확인해야합니다.
public enum UIDeviceOrientation : Int {
case unknown
case portrait // Device oriented vertically, home button on the bottom
case portraitUpsideDown // Device oriented vertically, home button on the top
case landscapeLeft // Device oriented horizontally, home button on the right
case landscapeRight // Device oriented horizontally, home button on the left
case faceUp // Device oriented flat, face up
case faceDown // Device oriented flat, face down
}
기본적으로 기기가 회전이 가능하게끔 true로 설정되어있습니다.
회전을 지원하기 위해서는 beginGeneratingDeviceOrientationNotifications()를 활성화 시켜줘야합니다.
print(device.isGeneratingDeviceOrientationNotifications) // true
device.beginGeneratingDeviceOrientationNotifications() // Orientation을 지원하기위해 사용
//반대로는 endGeneratingDeviceOrientationNotifications()가 있습니다.
실행해보면 화면이 돌아가는것을 볼 수가있습니다. 또 반대로 endGeneratingDeviceOrientationNotifications()를 설정하면 화면이 돌아가지 않는것을 확인 할 수 있습니다.
디바이스 회전 방향도 Notification으로 상태를 체크할 수 있습니다. (UIDeviceOrientationDidChange)
NotificationCenter.default.addObserver(
self, selector: #selector(orientationDidChange(_:)), name: .UIDeviceOrientationDidChange, object: nil
)
@objc func orientationDidChange(_ noti: Notification) {
print("\n-------------------- [ Orientation didChange ] --------------------\n")
if let device = noti.object as? UIDevice {
print("Device Orientation :", device.orientation)
}
print("StatusBar Orientation :", UIApplication.shared.statusBarOrientation)
}
출력화면
-------------------- [ Orientation didChange ] --------------------
true
Device Orientation : UIDeviceOrientation
StatusBar Orientation : UIInterfaceOrientation
StatusBar Orientation은 조금있다가 확인해보겠습니다.
회전을 감지해서 상태가 UIDeviceOrientation이라는 것을 감지했지만 오른쪽, 왼쪽인지 정확한 상태가 나오지 않습니다.
어떤상태인지 디버깅창에 자세히 보이게 하기위해 CustomStringConvertible을 통해 상세히 나타내보도록 하겠습니다.
extension UIDeviceOrientation: CustomStringConvertible {
public var description: String {
switch self {
case .unknown: return "unknown"
case .portrait: return "portrait"
case .portraitUpsideDown: return "portraitUpsideDown"
case .landscapeLeft: return "landscapeLeft"
case .landscapeRight: return "landscapeRight"
case .faceUp: return "faceUp"
case .faceDown: return "faceDown"
}
}
}
extension UIInterfaceOrientation: CustomStringConvertible {
public var description: String {
switch self {
case .unknown: return "unknown"
case .portrait: return "portrait"
case .portraitUpsideDown: return "portraitUpsideDown"
case .landscapeLeft: return "landscapeLeft"
case .landscapeRight: return "landscapeRight"
}
}
}
위의 CustomStringConvertible을 통해 description을 활용하면 원하는 값으로 정보를 바꿀수 있습니다.
다시한번 출력값을 확인해보겠습니다.
출력화면
-------------------- [ Orientation didChange ] --------------------
true
Device Orientation : landscapeRight
StatusBar Orientation : landscapeLeft
기기를 오른쪽으로 돌렸을때(home button쪽이 왼쪽인 상태를 말합니다.) 정확히 Device Orientation에 landscapeRight라고 잘 나옵니다.
하지만 StatusBar Orientation이라는 상태바의 위치를 나타내는 놈은 반대를 가리키고 있습니다.
UIInterfaceOrientation 과 UIDeviceOrientation 의 차이 주의
UIApplication.shared.statusBarOrientation - statusBar 의 위치 기준입니다.
public enum UIInterfaceOrientation : Int {
case unknown
case portrait
case portraitUpsideDown
case landscapeLeft
case landscapeRight
}
-
UIDeviceOrientation : 디바이스 기준
-
UIInterfaceOrientation : 컨텐츠 기준
-
UIDeviceOrientation.landscapeRight = UIInterfaceOrientation.landscapeLeft
-
UIDeviceOrientation.landscapeLeft = UIInterfaceOrientation.landscapeRight
디바이스를 잘 살펴보면 디바이스를 왼쪽으로(landscapeLeft)로 돌리면 화면은 오른쪽으로 회전하는걸 볼 수 있습니다. 즉 화면(컨텐츠)는 오른쪽으로 회전하니까 디바이스는 왼쪽(landscapeLeft) 회전할때 화면은 오른쪽(landscapeRight)인겁니다.
이로써 두 가지의 Orientation을 사용할때는 주의해서 사용해야됩니다.
'iOS' 카테고리의 다른 글
[iOS] Photos Framework (0) | 2019.07.25 |
---|---|
[iOS] iOS Realm이란? (0) | 2019.07.24 |
[iOS] GCD(Grand Central Dispatch) (0) | 2019.07.24 |
ARC(Automatic Reference Counting) (0) | 2019.07.24 |
[iOS] 단축키(Shortcuts) (0) | 2019.07.24 |