앱에서 코드로 지정하여 notification center에 notification을 전달하는 것을 제외하고도 인터페이스 또는 기기의 변경 사항을 매번 notification center에 전달합니다. 다른 설정없이 작동하지만 그것들은 UIKit 클래스의 type propertie 입니다. 사용하는 경우는 드물지만 특정 기능은 유용하게 사용될 수 있습니다.
예를 들어, UIKit 프레임워크는 앱의 창을 만들고 키보드 상태를 보고하기 위해 알림을 게시하는 데 사용되는 UIWindow라는 클래스를 정의합니다. 다음은 키보드에서 자주 사용되는 notification 들입니다.
keyboardDidShowNotification - 키보드가 나타났을 때 발생하는 notification
keyboardDidHideNotification - 키보드가 사라졌을 때 발생하는 notification
다음은 UIKit의 UIWindow 클래스를 이용하여 키보드의 상태를 이름으로 한 notification을 center에 등록하는 코드입니다.
@Observable class ApplicationData {
@ObservationIgnored let center = NotificationCenter.default
var scrollOffset: CGFloat = 0
init() {
Task(priority: .background) {
await receiveNotificationOpen()
}
Task(priority: .background) {
await receiveNotificationClose()
}
}
func receiveNotificationOpen() async {
let name = await UIWindow.keyboardDidShowNotification
for await _ in center.notifications(named: name, object: nil) {
await MainActor.run {
scrollOffset = -20
}
}
}
func receiveNotificationClose() async {
let name = await UIWindow.keyboardDidHideNotification
for await _ in center.notifications(named: name, object: nil) {
await MainActor.run {
scrollOffset = 0
}
}
}
}
다음은 화면에서 TextField를 통해서 키보드를 올렸다가 내리는 코드로 키보드의 유무에 따라서 ScrollView의 크기가 달라지는 코드입니다.
struct ContentView: View {
@Environment(ApplicationData.self) private var appData
@FocusState var focusTitle: Bool
@State private var inputTitle: String = ""
var body: some View {
ScrollView {
VStack {
Image(.spot1)
.resizable()
.scaledToFit()
HStack {
TextField("Insert Title", text: $inputTitle)
.textFieldStyle(.roundedBorder)
.focused($focusTitle)
Button("Save") {
focusTitle = false
}
}
Spacer()
}.padding()
}
.offset(CGSize(width: 0, height: appData.scrollOffset))
}
}
가로 세로에 맞게 대응하기
아이폰의 가로 세로 유무를 확인하고 확인한 결과에 따라서 현재 아이폰의 상태에 맞게 뷰를 나타내는 방법도 있습니다.
다음 코드는 NotificationCenter를 사용하여 notification의 이름은 UIDevice.orientationDidChangeNotification 로 지정하고 MainActor 를 사용하여 main thread 에서 isLandscape 의 상태를 바꾸는 코드입니다. 만약 현재 아이폰이 가로 상태라면 true 세로 상태라면 false 입니다.
@Observable class ApplicationData {
var isLandscape: Bool = false
init() {
Task(priority: .background) {
await receiveNotification()
}
}
func receiveNotification() async {
let center = NotificationCenter.default
let name = await UIDevice.orientationDidChangeNotification
print(name)
for await _ in center.notifications(named: name, object: nil) {
await MainActor.run {
let device = UIDevice.current
let orientation = device.orientation
isLandscape = orientation.isLandscape
}
}
}
}
아래는 View 부분입니다.
struct ContentView: View {
@Environment(ApplicationData.self) private var appData
var body: some View {
Group {
if !appData.isLandscape {
VStack(spacing: 0) {
HeaderView(isCompact: true)
BodyView()
}
} else {
HStack(spacing: 0) {
HeaderView(isCompact: false)
BodyView()
}
}
}.ignoresSafeArea()
.onAppear {
let device = UIDevice.current
device.beginGeneratingDeviceOrientationNotifications()
}
.onDisappear {
let device = UIDevice.current
device.endGeneratingDeviceOrientationNotifications()
}
}
}
struct HeaderView: View {
let isCompact: Bool
var body: some View {
Text("Food Menu")
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: isCompact ? 150 : .infinity)
.background(Color.yellow)
}
}
struct BodyView: View {
var body: some View {
Text("Content Title")
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity)
.background(Color.gray)
}
}
#Preview {
ContentView()
.environment(ApplicationData())
}
'SwiftUI' 카테고리의 다른 글
AppDelegate (UIWindowSceneDelegate, UIApplicationDelegate) (0) | 2024.01.16 |
---|---|
User Notification 알림 표시하기 (0) | 2024.01.16 |
TapGesture (0) | 2024.01.06 |
RotateGesture (0) | 2024.01.06 |
Transferable (0) | 2023.12.22 |