I’m at present making a facet menu for an iOS app in SwiftUI and having navigation points. Navigating to a view from the facet menu is ok, however the view will present up contained in the facet menu itself, slightly than it being like Twitter, the place the facet menu is dismissed after which the view opens up within the app. I’ve made a reproducible mock of the difficulty, which could be seen beneath.
First is the info mannequin for the facet menu:
struct SideMenuOBJ: Identifiable, Hashable {
let id = UUID()
let picture: UIImage
let label: String
var vc: UIViewController?
}
struct SideMenuRepository {
static func profile () -> [SideMenuOBJ] {
let base = [
SideMenuOBJ(image: UIImage(systemName: "person.circle")!, label: "Profile"),
SideMenuOBJ(image: UIImage(systemName: "list.clipboard")!, label: "Account Summary"),
SideMenuOBJ(image: UIImage(systemName: "fossil.shell")!, label: "History")
]
return base
}
}
The Drawer
creates the facet menu:
public struct Drawer: View {
@Binding non-public var isOpened: Bool
non-public let menu: Menu
non-public let content material: Content material
public init(
isOpened: Binding,
@ViewBuilder menu: () -> Menu,
@ViewBuilder content material: () -> Content material
) {
_isOpened = isOpened
self.menu = menu()
self.content material = content material()
}
public var physique: some View {
ZStack(alignment: .main) {
content material
if isOpened {
Shade.clear
.contentShape(Rectangle())
.onTapGesture {
if isOpened {
isOpened.toggle()
}
}
menu
.transition(.transfer(edge: .main))
.zIndex(1)
}
}
.animation(.spring(), worth: isOpened)
.atmosphere(.drawerPresentationMode, $isOpened.mappedToDrawerPresentationMode())
}
}
public struct DrawerPresentationMode {
@Binding non-public var _isOpened: Bool
init(isOpened: Binding) {
__isOpened = isOpened
}
public var isOpened: Bool {
_isOpened
}
mutating func open() {
if !_isOpened {
_isOpened = true
}
}
mutating func shut() {
if _isOpened {
_isOpened = false
}
}
}
extension Binding the place Worth == Bool {
func mappedToDrawerPresentationMode() -> Binding {
Binding(
get: {
DrawerPresentationMode(isOpened: self)
},
set: { newValue in
self.wrappedValue = newValue.isOpened
}
)
}
}
extension DrawerPresentationMode {
static var placeholder: DrawerPresentationMode {
DrawerPresentationMode(isOpened: .fixed(false))
}
}
non-public struct DrawerPresentationModeKey: EnvironmentKey {
static var defaultValue: Binding = .fixed(.placeholder)
}
extension EnvironmentValues {
public var drawerPresentationMode: Binding {
get { self[DrawerPresentationModeKey.self] }
set { self[DrawerPresentationModeKey.self] = newValue }
}
}
The view mannequin is fairly easy:
class SideMenuViewModel: ObservableObject {
@Revealed var profileOptions: [SideMenuOBJ] = SideMenuRepository.profile()
@ViewBuilder
func handleProfileOptionTap(choice: SideMenuOBJ?) -> some View {
swap choice?.label {
case "My Profile":
ProfileView()
case "Account":
AccountSummaryView()
case "My Takes Historical past":
Historical past()
default:
EmptyView()
}
}
}
Lastly, right here is the facet menu and the mother or father view that calls the facet menu:
struct SideMenu: View {
@ObservedObject var viewModel: SideMenuViewModel
@State non-public var selectedOption: SideMenuOBJ?
@State non-public var isNavigating: Bool = false
var physique: some View {
NavigationStack {
ZStack {
ScrollView {
VStack(alignment: .main, spacing: 16) {
VStack(alignment: .main, spacing: 12) {
ForEach(viewModel.profileOptions) { choice in
Button {
selectedOption = choice
isNavigating.toggle()
} label: {
HStack {
Picture(uiImage: choice.picture)
.renderingMode(.template)
.foregroundColor(.major)
.body(width: 24, peak: 24)
Textual content(choice.label)
.fontWeight(.daring)
.foregroundStyle(Shade.major)
.font(.physique)
.padding(.vertical, 8)
.padding(.horizontal)
.cornerRadius(8)
}
.cornerRadius(8)
}
}
}
.padding(.horizontal)
}
}
.scrollIndicators(.by no means)
}
.navigationDestination(merchandise: $selectedOption) { choice in
viewModel.handleProfileOptionTap(choice: choice)
}
}
}
}
struct SideMenuIssueApp: App {
@State non-public var showSideMenu: Bool = false
var physique: some Scene {
WindowGroup {
Drawer(isOpened: $showSideMenu) {
ZStack {
SideMenu(viewModel: SideMenuViewModel())
.body(width: 270)
}
} content material: {
contentView
}
}
}
var contentView: some View {
NavigationStack {
TabView {
ContentView()
.tabItem {
Label("Content material View", systemImage: "tray")
}
}
.toolbar {
ToolbarItem(placement: .topBarLeading) {
Button {
showSideMenu.toggle()
} label: {
Picture(systemName: "menucard")
}
}
}
}
}
}
ProfileView, AccountSummaryView, and HistoryView are all default views for navigation testing. What am I doing mistaken regarding the navigation that is making the chosen view present up within the Aspect Menu as a substitute of on the app itself? All assistance is vastly appreciated!