[iOS/SwiftUI] TCA Sheet 구현하기
https://developer.apple.com/documentation/swiftui/view/sheet(ispresented:ondismiss:content:)
sheet(isPresented:onDismiss:content:) | Apple Developer Documentation
Presents a sheet when a binding to a Boolean value that you provide is true.
developer.apple.com
TCA 구조로 만들어진 프로젝트에서 Sheet 를 구현해보자!
nonisolated
func sheet<Content>(
isPresented: Binding<Bool>,
onDismiss: (() -> Void)? = nil,
@ViewBuilder content: @escaping () -> Content
) -> some View where Content : View
바인딩중인 Bool 값이 참일때 시트를 표시한다.
isPresented
수정자의 클로저에서 만든 시트를 표시할지 여부를 결정하는 부울 값에 대한 바인딩입니다 content.
onDismiss
시트를 닫을 때 실행할 닫힘.
content
시트의 내용을 반환하는 클로저.
기본적인 사용방법
struct ParentView : View {
@State var store: StoreOf<HomeFeature>
var body : some View {
VStack{
Text("홈")
}
.sheet(isPresented: $store.falg, onDismiss: didDismiss) {
// 이 뷰가 모달로 표시됩니다.
ChildView(store: store)
.presentationDetents([.medium, .large])
.presentationCompactAdaptation(.none)
}
}
func didDisMiss() {
// Sheet가 닫힐때 실행되는 함수
}
}
나는 TCA 방식으로 만들어
enum Action: BindableAction {
case openDecoration
case closeDecoration
case binding( BindingAction < State >)
}
var body : some ReducerOf <Self> {
BindingReducer ()
Reduce { state, action in
switch action {
case .binding(\.decoration) :
print("click deco : ", state.decoration)
return .none
case .binding( _ ):
return .none
case .openDecoration:
state.decoration = true
return .none
case .closeDecoration:
state.decoration = false
return .none
이렇게 만들고 didDismiss 함수안에 closeDecoration Action을 선언해 두었는데
func didDismiss() {
store.send(.closeDecoration)
}
closeDecoration Action이 실행되지 않는데도 정상적으로 작동하는 걸 확인했다.
그 이유는 sheet가 닫힐때 자동으로 바인딩 Bool 값이 toggle되어 Bool 값 toggle 메서드를 넣어주지 않아도 자동으로 toggle되는 것을 확인할 수 있다.
Presentation 설정
두가지가 대표적으로 사용된다.
- presentationDetents
- presentationCompactAdaptation
1. PresentationDetents
https://developer.apple.com/documentation/swiftui/view/presentationdetents(_:)
presentationDetents(_:) | Apple Developer Documentation
Sets the available detents for the enclosing sheet.
developer.apple.com
SwiftUI에서 시트를 표시할 때, 그 시트의 높이를 결정하는 "detent"를 설정하는 것을 의미한다.
nonisolated
func presentationDetents(
_ detents: Set<PresentationDetent>,
selection: Binding<PresentationDetent>
) -> some View
Parameter
detents
- 시트가 멈출 수 있는 높이의 집합입니다.
- 여러 개의 detent를 제공하면 사용자는 시트를 드래그하여 크기를 조정할 수 있습니다.
- 예를 들어, 화면의 절반 높이 또는 전체 화면 높이에서 시트가 멈출 수 있게 설정할 수 있습니다.
selection
- 현재 선택된 detent를 나타내는 Binding입니다.
- 이 값은 detents 매개변수로 제공한 detent 중 하나와 일치해야 합니다.
- 이를 통해 프로그래밍적으로 시트의 높이를 제어하거나 변경할 수 있습니다.
사용예시
struct ContentView: View {
@State private var showSettings = false
@State private var settingsDetent = PresentationDetent.medium
var body: some View {
Button("View Settings") {
showSettings = true
}
.sheet(isPresented: $showSettings) {
SettingsView()
.presentationDetents(
[.medium, .large],
selection: $settingsDetent
)
}
}
}
detents 값은 시트를 올리고 내릴때 고정되는 크기를 의미하고
selection은 처음 열렸을때 보이는 크기를 지정한다. 생략시 detents값중 처음에 온 값으로 설정된다!
그런데 detents 값을 하나만 선택했을때는 sheet 상단의 하얀색 바는 보이지 않게 된다.
.presentationDetents([.medium])
.presentationCompactAdaptation(.none)
.presentationDetents([.medium, .large])
.presentationCompactAdaptation(.none)
- func presentationDetents(Set<PresentationDetent>, selection: Binding<PresentationDetent>) -> some View:
- 이 함수는 시트가 멈출 수 있는 높이, 즉 "detent"를 설정합니다.
- 여러 detent를 지정할 수 있으며, 현재 선택된 detent를 프로그래밍적으로 제어할 수 있습니다.
- 예를 들어, 시트를 화면의 절반 높이로 설정하거나 전체 화면을 덮도록 설정할 수 있습니다.
- func presentationContentInteraction(PresentationContentInteraction) -> some View:
- 이 함수는 시트에서 스와이프 제스처의 동작을 설정합니다.
- 사용자가 시트를 드래그하거나 스와이프할 때 시트가 어떻게 반응할지 결정합니다.
- func presentationDragIndicator(Visibility) -> some View:
- 이 함수는 시트 상단에 표시되는 드래그 인디케이터의 가시성을 설정합니다.
- 드래그 인디케이터는 사용자가 시트를 드래그하여 닫거나 크기를 조절할 수 있다는 시각적 힌트를 제공합니다.
- struct PresentationDetent:
- 시트가 자연스럽게 멈추는 특정 높이를 나타내는 타입입니다.
- 예를 들어, 시트가 화면의 절반 높이에서 멈추거나 전체 화면을 덮도록 설정할 수 있습니다.
- protocol CustomPresentationDetent:
- 계산된 높이로 사용자 정의 detent를 정의하는 프로토콜입니다.
- 개발자가 특정 요구 사항에 맞게 시트의 높이를 커스터마이징할 수 있도록 합니다.
- struct PresentationContentInteraction:
- 프레젠테이션이 스와이프 제스처에 반응하는 방식을 설정하는 데 사용되는 동작입니다.
- 예를 들어, 사용자가 시트를 드래그할 때 시트가 닫히는지, 아니면 다른 동작을 수행하는지 설정할 수 있습니다.
2. presentationCompactAdaptation(_:)
https://developer.apple.com/documentation/swiftui/view/presentationcompactadaptation(_:)
presentationCompactAdaptation(_:) | Apple Developer Documentation
Specifies how to adapt a presentation to compact size classes.
developer.apple.com
프레젠테이션 화면을 컴팩트 클래스크기에 맞추는 메서드다!
"가로 방향의 iPhone과 같이 수직으로 컴팩트한 환경에서는 시트 프레젠테이션이 자동으로 전체 화면 커버로 나타나도록 조정됩니다. 또는 수정자를 사용하여 이 동작을 재정"의합니다
사용예시
struct ContentView: View {
@State private var showSettings = false
var body: some View {
Button("View Settings") {
showSettings = true
}
.sheet(isPresented: $showSettings) {
SettingsView()
.presentationDetents([.medium, .large])
.presentationCompactAdaptation(.none)
}
}
}
그외 비슷한 메서드
1. Sheet
- func sheet<Item, Content>(item: Binding<Item?>, onDismiss: (() -> Void)?, content: (Item) -> Content) -> some View
- 특정 항목(Item)을 데이터 소스로 사용하여 시트를 표시합니다. item 바인딩이 nil이 아닐 때 시트가 표시됩니다.
- 매개변수:
- item: 시트의 콘텐츠에 사용될 데이터 소스.
- onDismiss: 시트가 닫힐 때 실행될 선택적인 클로저.
- content: 시트의 콘텐츠를 정의하는 클로저.
2. Full-Screen Cover
- func fullScreenCover<Content>(isPresented: Binding<Bool>, onDismiss: (() -> Void)?, content: () -> Content) -> some View
- 주어진 불리언 값이 true일 때 화면을 최대한 덮는 모달 뷰를 표시합니다.
- 매개변수:
- isPresented: 모달 뷰가 표시될지 여부를 제어하는 바인딩.
- onDismiss: 모달 뷰가 닫힐 때 실행될 선택적인 클로저.
- content: 모달 뷰의 콘텐츠를 정의하는 클로저.
- func fullScreenCover<Item, Content>(item: Binding<Item?>, onDismiss: (() -> Void)?, content: (Item) -> Content) -> some View
- 특정 항목(Item)을 데이터 소스로 사용하여 화면을 최대한 덮는 모달 뷰를 표시합니다. item 바인딩이 nil이 아닐 때 모달 뷰가 표시됩니다.
3. Popover
- func popover<Item, Content>(item: Binding<Item?>, attachmentAnchor: PopoverAttachmentAnchor, content: (Item) -> Content) -> some View
- 특정 항목(Item)을 데이터 소스로 사용하여 팝오버를 표시합니다.
- 매개변수:
- item: 팝오버의 콘텐츠에 사용될 데이터 소스.
- attachmentAnchor: 팝오버가 화면에 부착될 위치를 정의하는 앵커.
- content: 팝오버의 콘텐츠를 정의하는 클로저.
- func popover<Content>(isPresented: Binding<Bool>, attachmentAnchor: PopoverAttachmentAnchor, content: () -> Content) -> some View
- 주어진 불리언 값이 true일 때 팝오버를 표시합니다.
- func popover<Item, Content>(item: Binding<Item?>, attachmentAnchor: PopoverAttachmentAnchor, arrowEdge: Edge, content: (Item) -> Content) -> some View
- 특정 항목(Item)을 데이터 소스로 사용하여 팝오버를 표시합니다. 팝오버의 화살표가 나타날 모서리(Edge)를 지정할 수 있습니다.
- func popover<Content>(isPresented: Binding<Bool>, attachmentAnchor: PopoverAttachmentAnchor, arrowEdge: Edge, content: () -> Content) -> some View
- 주어진 불리언 값이 true일 때 팝오버를 표시하며, 팝오버의 화살표가 나타날 모서리를 지정할 수 있습니다.
PopoverAttachmentAnchor
- 팝오버가 화면에 부착될 위치를 정의하는 앵커를 나타냅니다. (ex. rect, point 등의 값이 있습니다.)
공식문서를 보면서 공부를 하니 더 디테일하게 사용하고 차이점을 알 수 있어서 좋은 것 같다.