이번 포스팅에서는 사이드 바 메뉴 만들기를 해보도록 하겠습니다. 이번 포스팅이 완전한 사이드 바 메뉴는 아니지만 일단 최소한의 기능을 가지고 있는 사이드 바 메뉴를 만들어 보도록 하겠습니다.
예전에 개인 프로젝트에 사이드 바 메뉴를 활용한 적이 있는데요. 그 때는 외부 라이브러리를 활용해서 구현을 했습니다. 이번에는 직접 구현해보고자 합니다.
보통의 사이드 바 메뉴를 보면 측면에서 사이드 바 메뉴가 나오면 나머지 화면은 어두워지는데요. 이 View를 구현하기 위해서 ZStack을 활용해서 반투명한 검은색을 깔고 앞에 사이드 바 메뉴가 오도록 했습니다.
그리고 사이드 바 메뉴는 화면의 70%만 차지하도록 했습니다. 그리고 ZStack의 alignment를 활용해서 다음 사이드 바 메뉴가 우측에서 보이도록 했습니다.
private struct SettingSideBar: View {
var body: some View {
ZStack(alignment: .trailing) {
Color.black.opacity(0.5)
VStack {
// 사이드바 메뉴 내용
}
.frame(width: Constants.Size.deviceWidth * 0.7)
.background { Color.white }
}
}
}
원래 View에 사이드 메뉴를 추가할 때도 ZStack을 사용합니다. 사이드 메뉴가 보일 때는 기존의 View를 덮고 보여져야 합니다. 그리고 @State 변수를 하나 추가해서 사이드 메뉴가 보일지 말지를 결정하는 Bool 값을 만들어 놓도록 하겠습니다.
@State private var showSideBar: Bool = false
var body: some View {
ZStack {
ScrollView {
LazyVStack(spacing: 32) {
ForEach(viewModel.words, id: \.id) { word in
WordCell(word: word, frontType: viewModel.frontType, eventPublisher: viewModel.eventPublisher)
.frame(width: deviceWidth * 0.9, height: word.hasImage ? 200 : 100)
}
}
}
if showSideBar {
SettingSideBar()
}
}
.toolbar {
ToolbarItem {
HStack {
Button("랜덤") {
viewModel.shuffleWords()
}
Button("설정") {
showSideBar = true
}
}
}
}
}
사이드 바 메뉴들은 보통 드래그를 통해서 닫을 수 있습니다. 지금 만드는 사이드 바 메뉴는 우측에서 위치하므로 좌에서 우로 드래그 하며 닫히도록 만들겠습니다.
아래 기능을 위해서 @GestureState를 사용했는데요. 예전에도 포스팅한 적이 있는 기능입니다. 자세한 구현 방법은 이 포스팅을 참고해주세요. @GestureState와 offset을 활용해서 사이드 바 메뉴를 움직이도록 했습니다.
그리고 마지막으로 onEnded를 통해서 드래그한 거리가 화면의 35% 즉 사이드 바 메뉴의 반 이상이라면 사이드 바 메뉴가 닫히도록 했습니다. 사이드 바 메뉴가 닫히려면 부모 View의 @State 변수를 false로 바꾸어야 합니다. 해당 변수를 @Binding으로 받아와서 false로 바꾸어 줍니다.
extension StudyView {
private struct SettingSideBar: View {
@Binding var showSideBar: Bool
@GestureState private var dragAmount = CGSize.zero
private var dragGesture: some Gesture {
DragGesture(minimumDistance: 30, coordinateSpace: .global)
.onEnded { onEnded($0) }
.updating($dragAmount) { value, state, _ in
if value.translation.width > 0 {
state.width = value.translation.width
}
}
}
var body: some View {
ZStack(alignment: .trailing) {
Color.black.opacity(0.5)
.onTapGesture { showSideBar = false }
VStack {
Spacer()
Picker("", selection: $viewModel.studyMode) {
ForEach(StudyMode.allCases, id: \.self) {
Text($0.pickerText)
}
}
.pickerStyle(.segmented)
.padding()
Picker("", selection: $viewModel.frontType) {
ForEach(FrontType.allCases, id: \.self) {
Text($0.pickerText)
}
}
.pickerStyle(.segmented)
.padding()
Spacer()
}
.frame(width: Constants.Size.deviceWidth * 0.7)
.background { Color.white }
.offset(x: dragAmount.width)
.gesture(dragGesture)
}
.ignoresSafeArea()
}
private func onEnded(_ value: DragGesture.Value) {
if value.translation.width > Constants.Size.deviceWidth * 0.35 {
showSideBar = false
}
}
}
}
아주 기본적인 기능만 가지고 있는 사이드 바 메뉴를 만들어 보았습니다. 결과물은 아래와 같습니다.
물론 다른 멋진 프레임워크들에 비하면 부족합니다만 그래도 구색은 갖춘 것 같습니다. 앞으로 등장 애니메이션, 모듈화 등의 추가적인 과제들을 구현해보도록 하겠습니다.