[SwiftUI] SideMenuViewからの画面遷移方法

## はじめに
こんにちは!
アプリ開発が好きで、Swiftの勉強をしている大学生です。
先日、投稿した記事で「物足りない機能」として取り上げた問題の一つを解消してみたいと思います。

https://qiita.com/kanta_Swift/items/e2298c813974ced24741

## 解決したい問題
SideMenuViewから画面遷移を行いたいと思い、単純に`NavigationLink`等を使うと以下のような問題が発生します。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3119753/6834af80-d80b-95d6-235f-357f184ec8e0.gif" width="350">

これを解決していきたいと思います。

## 完成イメージ

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3119753/004a8426-5f22-85b6-96ed-e478c2c3c0ff.gif" width="350">

## 実装イメージ
自分が思いついた解決方法のイメージは以下の通りです。
1. 画面遷移する際に`Frame`の`Offset`を元(HStackのViewのoffsetを0)に戻す。
1. HogeViewの上にViewが積み重なって、画面遷移する。
※SideMenuViewの上にViewが積み重なるような画面遷移をすると、うまくいかなかったです。

こんなイメージを持ちながら、前回のコードを使って追加でコーディングしていきます。

## 実装
まず遷移先のViewとなるFugaViewを用意します
```Swift
public struct FugaViewView: View {
    public var body: some View {
        Text("FugaFuga~~~")
    }
}
```
HogeViewでは以下のようにコードを追加します
```Swift
public struct HogeView: View {
    @Binding var isSideMenu: Bool
    @Binding var isPresented: Bool // 遷移するかどうかのフラグを定義
    
    public init(isSideMenu: Binding<Bool>, isPresented: Binding<Bool>) {
        self._isSideMenu = isSideMenu
        self._isPresented = isPresented // 追加箇所
    }
    
    public var body: some View {
        NavigationStack {
            VStack {
                Button {
                    isSideMenu.toggle()
                } label: {
                    Text("open SideMenu")
                }
            }
            .frame(maxHeight: .infinity)
            // 追加箇所
            .navigationDestination(isPresented: $isPresented) {
                FugaViewView()
            }
        }
    }
}


```
あくまで遷移するタイミングはSideMenuViewのイベント発生時なので、`isPresented`というフラグとなる変数を定義し、この値をSideMenuViewから変更することで遷移するという流れです。
SideMenuViewでは以下のようにコードを追加します。
```Swift

public struct SideMenuView: View {
    @Binding var isSideMenu: Bool
    @Binding var isPresented: Bool // HogeViewと同じく画面遷移フラグ
    
    public init(isSideMenu: Binding<Bool>, isPresented: Binding<Bool>) {
        self._isSideMenu = isSideMenu
        self._isPresented = isPresented // 追加箇所
    }
    public var body: some View {
        NavigationStack {
            VStack(alignment: .leading, spacing: 0) {
                ScrollView {
                    VStack(spacing: 15) {
                        Text("HogeHoge~~~")
                            .font(.system(size: 24).bold())
                        
                        Text("Side Menu")
                        
                        Button {
                            isPresented.toggle() // ここでフラグの値を変更！
                        } label: {
                            Text("遷移したいよ〜〜")
                        }
                    }
                    .padding(.top, 20)
                }
            }
            .frame(width: getScreenWidth() - 90)
            .background {
                Color.primary
                    .opacity(0.04)
                    .ignoresSafeArea(.container, edges: .vertical)
            }
            .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .leading)
        }

    }
}

```

最後にConnecterViewに以下のようにコードを追加します

```Swift
public struct ConnecterView: View {
    @State private var isSideMenu = false
    @State private var offset: CGFloat = 0
    @State private var isPresented = false // 追加箇所

    public init() {}
    
    public var body: some View {
        let sideBarWidth = getScreenWidth() - 90
        HStack(spacing: 0) {
            SideMenuView(isSideMenu: $isSideMenu, isPresented: $isPresented)
            
            HogeView(isSideMenu: $isSideMenu, isPresented: $isPresented)
                .frame(width: getScreenWidth())
                .overlay {
                    Rectangle()
                        .fill(
                            Color.primary
                                .opacity(Double((offset / sideBarWidth) / 5))
                        )
                        .ignoresSafeArea(.container, edges: .vertical)
                        .onTapGesture {
                            isSideMenu.toggle()
                        }
                }
        }
        .frame(width: getScreenWidth() + sideBarWidth)
        .offset(x: -sideBarWidth / 2)
        .offset(x: offset)
        .onChange(of: isSideMenu) { _ in
            if isSideMenu && offset == 0 {
                withAnimation {
                    offset = sideBarWidth
                }
            }
            
            if !isSideMenu && offset == sideBarWidth {
                withAnimation {
                    offset = 0
                }
            }
        }
        .onChange(of: isPresented) { newValue in // 追加箇所
            withAnimation {
                offset = 0
            }
        }
    }
}



```
これで実装できました！

あともう一つのDragとともに開く実装ができれば結構いい感じな気がしております。

## 終わりに
誰かの役に立つことができていれば幸いです。
アウトプットを頑張ろうと思っているので温かい目で見ていただけると嬉しいです。