pilotman
pilotman

Reputation: 675

iOS: How to switch views with a Side Menu, SwitftUI

firstly I am really new to iOS development and Swift (2 weeks coming here from PHP :))

I am trying to build a iOS application that has a side menu. And my intention is when I click on a item in the menu the new view will appear on screen like the 'HomeViewController' and for each consequent item like example1, 2 etc (In place of the menu button, Note I will be adding the top nav bar soon to open the menu) I am wondering how I can accomplish this feature?

Thanks

ContentView.swift

import SwiftUI

struct MenuItem: Identifiable {
    var id = UUID()
    let text: String
}

func controllView(clickedview:String) {
print(clickedview)
}

struct MenuContent: View{
    
    let items: [MenuItem] = [
    MenuItem(text: "Home"),
    MenuItem(text: "Example1"),
    MenuItem(text: "Example2"),
    MenuItem(text: "Example3")
    ]
    
    
    var body: some View {
        ZStack {
            Color(UIColor(red: 33/255.0, green: 33/255.0, blue: 33/255.0, alpha: 1))
            
            VStack(alignment: .leading, spacing: 0) {
                
                ForEach(items) {items in
                    HStack {
                        Text(items.text)
                            .bold()
                            .font(.system(size: 20))
                            .multilineTextAlignment(/*@START_MENU_TOKEN@*/.leading/*@END_MENU_TOKEN@*/)
                            .foregroundColor(Color.white)
                        Spacer()
                    }
                    .onTapGesture {
                        controllView(clickedview: items.text)
                    }
                    .padding()
                    
                    Divider()
                }
                
                Spacer()
                
            }
            .padding(.top, 40)
        }
    }
}

struct SideMenu: View {
    let width: CGFloat
    let menuOpen: Bool
    let toggleMenu: () -> Void
    
    var body: some View {
        ZStack {
            //Dimmed backgroud
            GeometryReader { _ in
                EmptyView()
            }
            .background(Color.gray.opacity(0.15))
            .opacity(self.menuOpen ? 1 : 0)
            .animation(Animation.easeIn.delay(0.25))
            .onTapGesture {
                self.toggleMenu()
            }
            
            //Menucontent
            HStack {
                MenuContent()
                    .frame(width: width)
                    .offset(x: menuOpen ? 0 : -width)
                    .animation(.default)
                
                Spacer()
            }
            
        }
    }
}

struct ContentView: View {
    @State var menuOpen = false
    
    var body: some View {
        
        let drag = DragGesture()
                    .onEnded {
                        if $0.translation.width < -100 {
                            if menuOpen {
                            withAnimation {
                                print("Left")
                                menuOpen.toggle()
                            }
                        }
                    }
                        if $0.translation.width > -100 {
                            if !menuOpen {
                            withAnimation {
                                print("Right")
                                menuOpen.toggle()
                            }
                        }
                    }
                }

        ZStack {
            if !menuOpen {
            Button(action: {
                self.menuOpen.toggle()
            }, label: {
                Text("Open Menu")
                    .bold()
                    .foregroundColor(Color.white)
                    .frame(width: 200, height: 50, alignment: /*@START_MENU_TOKEN@*/.center/*@END_MENU_TOKEN@*/)
                    .background(Color(.systemBlue))
            })
            }
            SideMenu(width: UIScreen.main.bounds.width/1.6, menuOpen: menuOpen, toggleMenu: toggleMenu)
        }
        .edgesIgnoringSafeArea(.all)
        .gesture(drag)
    }
    
    func toggleMenu(){
        menuOpen.toggle()
    }

}




struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

The on tap command from the above code:

.onTapGesture {
   controllView(clickedview: items.text)
}

HomeViewController.swift

import UIKit
import WebKit
class HomeViewController: UIViewController, WKNavigationDelegate {

    var webView: WKWebView!
    
    override func loadView() {
        webView = WKWebView()
        webView.navigationDelegate = self
        view = webView
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        let url = URL(string: "https://developer.apple.com")!
        webView.load(URLRequest(url: url))
    }
}

Error Screenshot

Upvotes: 0

Views: 740

Answers (1)

jnpdx
jnpdx

Reputation: 52416

You'll need a couple of ingredients:

  • A way to store the state of the currently active view
  • A way to communicate the state between your menu and main content view

For the first one, I made an enum that listed the different types of views (ViewType) and added it to your MenuItem model.

For the second, you can pass state via a @Binding from parent to child views and back up the chain.

struct MenuItem: Identifiable {
    var id = UUID()
    let text: String
    let viewType : ViewType
}

enum ViewType {
    case home, example1, example2, example3
}

struct MenuContent: View{
    @Binding var activeView : ViewType
    
    let items: [MenuItem] = [
        MenuItem(text: "Home", viewType: .home),
        MenuItem(text: "Example1", viewType: .example1),
        MenuItem(text: "Example2", viewType: .example2),
        MenuItem(text: "Example3", viewType: .example3)
    ]
    
    
    var body: some View {
        ZStack {
            Color(UIColor(red: 33/255.0, green: 33/255.0, blue: 33/255.0, alpha: 1))
            
            VStack(alignment: .leading, spacing: 0) {
                
                ForEach(items) { item in
                    HStack {
                        Text(item.text)
                            .bold()
                            .font(.system(size: 20))
                            .multilineTextAlignment(.leading)
                            .foregroundColor(Color.white)
                        Spacer()
                    }
                    .onTapGesture {
                        activeView = item.viewType
                    }
                    .padding()
                    
                    Divider()
                }
                
                Spacer()
                
            }
            .padding(.top, 40)
        }
    }
}

struct SideMenu: View {
    let width: CGFloat
    let menuOpen: Bool
    let toggleMenu: () -> Void
    @Binding var activeView : ViewType
    
    var body: some View {
        ZStack {
            //Dimmed backgroud
            GeometryReader { _ in
                EmptyView()
            }
            .background(Color.gray.opacity(0.15))
            .opacity(self.menuOpen ? 1 : 0)
            .animation(Animation.easeIn.delay(0.25))
            .onTapGesture {
                self.toggleMenu()
            }
            
            //Menucontent
            HStack {
                MenuContent(activeView: $activeView)
                    .frame(width: width)
                    .offset(x: menuOpen ? 0 : -width)
                    .animation(.default)
                
                Spacer()
            }
            
        }
    }
}

struct ContentView: View {
    @State private var menuOpen = false
    @State private var activeView : ViewType = .home
    
    var body: some View {
        
        let drag = DragGesture()
            .onEnded {
                if $0.translation.width < -100 {
                    if menuOpen {
                        withAnimation {
                            print("Left")
                            menuOpen.toggle()
                        }
                    }
                }
                if $0.translation.width > -100 {
                    if !menuOpen {
                        withAnimation {
                            print("Right")
                            menuOpen.toggle()
                        }
                    }
                }
            }
        
        ZStack {
            
            VStack {
                if !menuOpen {
                    Button(action: {
                        self.menuOpen.toggle()
                    }, label: {
                        Text("Open Menu")
                            .bold()
                            .foregroundColor(Color.white)
                            .frame(width: 200, height: 50, alignment: .center)
                            .background(Color(.systemBlue))
                    })
                }
                switch activeView {
                case .home:
                    HomeViewControllerRepresented()
                case .example1:
                    Text("Example1")
                case .example2:
                    Text("Example2")
                case .example3:
                    Text("Example3")
                }
            }
            
            SideMenu(width: UIScreen.main.bounds.width/1.6,
                     menuOpen: menuOpen,
                     toggleMenu: toggleMenu,
                     activeView: $activeView)
             .edgesIgnoringSafeArea(.all)
        }
        .gesture(drag)
    }
    
    func toggleMenu(){
        menuOpen.toggle()
    }
}

struct HomeViewControllerRepresented : UIViewControllerRepresentable {
    func makeUIViewController(context: Context) -> HomeViewController {
        HomeViewController()
    }
    
    func updateUIViewController(_ uiViewController: HomeViewController, context: Context) {
        
    }
}

Upvotes: 1

Related Questions