# Alert
SwiftUI 에서 제공하는 알럿창을 사용해본다. 기존에는 Alert
생성자를 통해 직접 호출하는 방식이었다면 iOS15 이후부터는 .alert
모디파이어 내에서 처리하게 된다.
struct Alert_Tutorials: View {
@State private var message = ""
@State private var showAlert = false
var body: some View {
VStack {
Text(message)
.font(.largeTitle)
Button {
showAlert = true
} label: {
Text("Show Alert")
}
.padding()
.alert("경고창", isPresented: $showAlert) {
// 1. 확인버튼
Button("확인") {
message = "확인"
}
// 2. 취소버튼
Button(role: .cancel) {
message = "취소"
} label: {
Text("취소")
}
} message: {
Text("조심하세요!")
}
}
}
}
.alert
모디파이어는 위치에 구애받지 않고, @State
를 기반으로 로직 설계만 정상적으로 이루어지면 모든 뷰에 모디파이어로 등록해도 된다.
다만 통상적인 경우 다음과 같은 위치에서 alert 모디파이어를 호출한다.
- 알럿창의 트리거가 되는 곳 (버튼 등)
- 트리거 로직 설계가 애매한 경우 취상위 컨테이너
만약 알럿창이 참조하는 데이터가 있는 경우 클로저 바깥의 특정 값을 가리키는 형태보다 presenting
파라미터를 갖는 alert
모디파이어를 호출하는 것이 좋다.
// presenting parameter!
.alert("경고", isPresented: $showImageAlert, presenting: imageData) { data in
Button("필터 적용") {
message = data.filters.joined(separator: ", ") + "필터를 적용합니다."
}
Button(role: .cancel) {
message = "취소"
} label: {
Text("취소")
}
} message: { data in
Text("\(data.name) 파일에서 어떤 작업을할까요? \n촬영일자\(data.date)")
}
presenting
파라미터에 데이터를 전달하면 알럿 액션 클로저의 파라미터로 데이터가 전달된다.
# Confirmation Dialog
컨퍼메이션 다이얼로그는 하단에 노출되며 여러 선택지들 중 하나를 고를 때 사용하는 UI이다.
.confirmationDialog("컬러 선택", isPresented: $showDialog) {
Button("Red") {
color = .red
}
Button("Green") {
color = .green
}
Button("Blue") {
color = .blue
}
Button(role: .cancel) {
} label: {
Text("취소")
}
Button(role: .destructive) {
color = .black
} label: {
Text("초기화")
}
} message: {
Text("원하는 컬러를 선택해주세요")
}
동작 자체는 알럿 모디파이어와 거의 동일하다. 뷰를 띄우는 바인더 속성 및 메세지, 내부 컴포넌트들을 정의하면 된다. visibility
파라미터는 플랫폼별로 기본적으로 제공하는 형태가 달라지므로 굳이 파라미터 지정까지 할 필요는 없다.
confirmationDialog
는 내부에 선택지가 여러개 들어가므로 버튼 컴포넌트가 중복되어 들어가는 코드가 작성된다. 이를 개선하기 위해 Identifiable
프로토콜을 채택한 모델을 새로 정의한 뒤 ForEach
를 사용하여 구현하면 좋다.
.confirmationDialog("컬러 선택", isPresented: $showDialog, presenting: ColorData.samples) { colors in
// ForEach 활용
ForEach(colors) { item in
Button(item.title) {
color = item.color
}
}
Button(role: .cancel) {
} label: {
Text("취소")
}
Button(role: .destructive) {
color = .black
} label: {
Text("초기화")
}
} message: { _ in
Text("원하는 컬러를 선택해주세요")
}
# Sheet
카드 모달 혹은 풀스크린 모달을 구현하려는 경우 .sheet
모디파이어, .fullScreenCover
모디파이어를 사용하면 된다. 화면을 띄우기 위해 isPresented
파라미터에 불리언 바인딩 속성을 전달하는 것도 동일하다.
@State private var presentCardModal = false
@State private var presentFullscreen = false
var body: some View {
VStack {
Button {
presentCardModal = true
} label: {
Text("Card Modal")
}
.sheet(isPresented: $presentCardModal, content: {
ImageScene()
})
Button {
presentFullscreen = true
} label: {
Text("Fullscreen")
}
.fullScreenCover(isPresented: $presentFullscreen, content: {
ImageScene()
})
}
}
만약 모달로 띄운 뷰 내에서 특정 뷰 조작을 통해 dismiss
를 처리하고 싶은 경우 @Environment
프로퍼티 래퍼를 통해 dismiss
액션을 가져오면 된다.
@Environment(\.dismiss) var dismiss
Button {
dismiss()
} label: {
Text("닫기")
}
혹은 위의 방식 외에 바인딩 속성에 대한 참조를 모달 뷰 내에 직접 전달하는 방법도 있다. 예시 코드에서는 ImageScene
에 바인딩 속성을 전달한다.
Button {
presentFullscreen = true
} label: {
Text("Fullscreen")
}
.padding()
.fullScreenCover(isPresented: $presentFullscreen, content: {
ImageScene(presentModal: $presentFullscreen) // 여기!
})
모달로 띄워지는 뷰에서는 참조를 전달받을 속성을 @Binding
프로퍼티 래퍼로 선언한다. 값 할당 없이 타입 지정만 해두면 되고, 프리뷰에서 발생하는 에러는 정적 바인딩으로 처리한다.
struct ImageScene: View {
@Environment(\.dismiss) var dismiss
@Binding var presentModal: Bool
var body: some View {
Image("photo")
.overlay(alignment: .top) {
Button {
// dismiss()
presentModal = false // 속성값 바인딩
} label: {
Image(systemName: "x.circle")
.resizable()
.frame(width: 50, height: 50)
.foregroundColor(.white)
.padding()
}
}
}
}
#Preview {
// 정적 바인딩
ImageScene(presentModal: .constant(true))
}
모달을 열어주는 로직이 반드시 불리언일 필요는 없다. Identifiable
프로토콜을 채택하는 데이터라면 값이 옵셔널 바인딩 했을때 정상적으로 추출되는 경우 모달을 띄워주는 식으로 설계가 가능하다.
sheet
모디파이어에 item
속성을 사용하면 된다.
.sheet(item: $imageData) { data in
OptionalImageScene(imageData: data)
}
# Popover
SwiftUI
에서는 팝오버 기능을 제공한다. 작은 팝업창을 띄워줄때 사용하고 .popover
모디파이어를 사용한다.
var body: some View {
Button(action: {
showPopover = true
}, label: {
Text("Show Popover")
})
.padding()
.popover(isPresented: $showPopover) {
Form_Tutorials()
.frame(minWidth: 320, minHeight: 400)
}
}
isPresented
파라미터에 바인딩 속성을 전달하는 것은 모달 띄우는 것과 동일하다. 팝오버는 앵커를 지정할 수 있다. 팝오버 모디파이어 두 번째 파라미터로 attachmentAnchor
를 사용하는데, 특정 프레임 및 좌표를 전달하여 팝오버가 위치할 기준점을 지정하게 된다. 일반적으로 attachmentAnchor
파라미터는 기본값으로 사용한다.
팝오버는 자체 너비를 계산하지 않기 때문에 모디파이어 내부에 리턴되는 컨텐츠 뷰들에 대한 레이아웃을 .frame
모디파이어로 직접 지정해줘야 한다.
# Interactive Dismiss
카드모달의 경우 사용자 실수로 모달을 dismiss하여 입력중인 데이터가 소멸되는 경우도 발생할 수 있다.
이때 .interactiveDismissDisabled(불리언 바인딩)
모디파이어를 호출하면 파라미터로 전달되는 불리언 값에 따라 dismiss
여부를 결정할 수 있다. true가 전달되면 모달이 내려가지 않고 튕겨져 올라온다.
ComposeScene(edited: $edited)
.interactiveDismissDisabled(edited)
ComposeScene
은 커스텀 컴포넌트이고 edited
바인딩 프로퍼티 래퍼를 조절하여 dismiss
여부를 결정한다.