{"id":2785,"date":"2025-11-22T11:22:24","date_gmt":"2025-11-22T02:22:24","guid":{"rendered":"https:\/\/www.freelifemakers.org\/wordpress\/?p=2785"},"modified":"2025-11-23T18:04:55","modified_gmt":"2025-11-23T09:04:55","slug":"swift-bottomtabmenu-private","status":"publish","type":"post","link":"https:\/\/www.freelifemakers.org\/wordpress\/index.php\/2025\/11\/22\/swift-bottomtabmenu-private\/","title":{"rendered":"[Swift]\ud558\ub2e8\ud0ed\uba54\ub274\/BottomTabMenu"},"content":{"rendered":"\n<p>\ud83d\udc49\ud83c\udffb \ud558\ub2e8 \ud0ed \uba54\ub274,\ub4dc\ub86d\ub2e4\uc6b4 \uba54\ub274\uc5d0 \ub300\ud55c \uc124\uba85\uc785\ub2c8\ub2e4.<br>Description of the bottom tab menu and drop-down menu.<\/p>\n\n\n\n<p>\ud83d\udc49\ud83c\udffb \uc2a4\ud06c\ub9b0 \ud30c\uc77c\uc774 \ub530\ub85c \ubd84\ub9ac\ub418\uc5b4\uc788\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4.<br>The screen files are not separated separately.<\/p>\n\n\n\n<p>\ud83d\udc49\ud83c\udffb \ub098\uba38\uc9c0 \uc124\uba85\uc740 \ucf54\ub4dc \uc8fc\uc11d\uc744 \ucc38\uc870 \ud558\uc138\uc694<br>Please refer to the code comments for the rest of the explanation.<\/p>\n\n\n\n<p>1.\ucf54\ub4dc\/Code<\/p>\n\n\n\n<p>\u2714\ufe0f ContentView.swift<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\n\nimport SwiftUI\n\n\/\/ MARK: - \uba54\uc778 ContentView\nstruct ContentView: View {\n    var body: some View {\n        MainView()\n    }\n}\n\n\n\/\/ MARK: - Screen Enum\nenum Screen: String, CaseIterable {\n    case home = \"\ud648\/Home\"\n    case search = \"\uac80\uc0c9\/Search\"\n    case settings = \"\uc124\uc815\/Settings\"\n\n    var icon: String {\n        switch self {\n        case .home: return \"house.fill\"\n        case .search: return \"magnifyingglass\"\n        case .settings: return \"gearshape.fill\"\n        }\n    }\n\n    var title: String {\n        self.rawValue\n    }\n}\n\n\/\/ MARK: - Main View\nstruct MainView: View {\n    @State private var selectedTab: Screen = .home\n\n    var body: some View {\n        NavigationStack {\n            VStack(spacing: 0) {\n                \/\/ \uc0c1\ub2e8 App Bar \/ Top app bar\n                TopBar(title: selectedTab.title)\n\n                \/\/ \ubcf8\ubb38 (\ud0ed\uc5d0 \ub530\ub978 \ud654\uba74 \uc804\ud658) \/ Body (Screen switching according to tab)\n                ZStack {\n                    switch selectedTab {\n                    case .home:\n                        HomeView()\n                    case .search:\n                        SearchView()\n                    case .settings:\n                        SettingsView()\n                    }\n                }\n                .frame(maxWidth: .infinity, maxHeight: .infinity)\n                .background(Color(.systemBackground))\n\n                \/\/ \ud558\ub2e8 \ub124\ube44\uac8c\uc774\uc158 \ubc14 \/ Bottom navigation bar\n                BottomNavigationBar(selectedTab: $selectedTab)\n            }\n            .ignoresSafeArea(.keyboard)\n        }\n    }\n}\n\n\/\/ MARK: - Top Bar (\uc0c1\ub2e8 \uc571\ubc14)\nstruct TopBar: View {\n    var title: String\n    @State private var showLeftMenu = false\n    @State private var showRightMenu = false\n\n    var body: some View {\n        HStack {\n            \/\/ \uc67c\ucabd \uba54\ub274 \ubc84\ud2bc \/ left menu button\n            Menu {\n                Button(\"\uba54\ub2741\/Menu1\", action: { print(\"\uba54\ub2741 \uc120\ud0dd\") })\n                Button(\"\uba54\ub2742\/Menu2\", action: { print(\"\uba54\ub2742 \uc120\ud0dd\") })\n            } label: {\n                Image(systemName: \"line.horizontal.3\")\n                    .font(.title2)\n                    .padding(.leading, 16)\n            }\n\n            Spacer()\n\n            Text(title)\n                .font(.headline)\n\n            Spacer()\n\n            \/\/ \uc624\ub978\ucabd \ub4dc\ub86d\ub2e4\uc6b4 \uba54\ub274 \/ Right drop-down menu\n            Menu {\n                Button(\"\uc124\uc815 \ubcc0\uacbd\/Change Settings\", action: { print(\"\uc124\uc815 \ubcc0\uacbd \uc120\ud0dd \/ Select Change Settings\") })\n                Button(\"\ub85c\uadf8\uc544\uc6c3\/Log out\", action: { print(\"\ub85c\uadf8\uc544\uc6c3 \uc120\ud0dd \/ Select logout\") })\n            } label: {\n                Image(systemName: \"ellipsis\")\n                    .rotationEffect(.degrees(90))\n                    .font(.title2)\n                    .padding(.trailing, 16)\n            }\n        }\n        .frame(height: 50)\n        .background(Color(.systemGray6))\n    }\n}\n\n\/\/ MARK: - Bottom Navigation Bar (\ud558\ub2e8 \ud0ed\ubc14)\nstruct BottomNavigationBar: View {\n    @Binding var selectedTab: Screen\n\n    var body: some View {\n        HStack {\n            ForEach(Screen.allCases, id: \\.self) { screen in\n                Spacer()\n\n                VStack {\n                    Image(systemName: screen.icon)\n                        .font(.system(size: 22))\n                    Text(screen.title)\n                        .font(.caption)\n                }\n                .foregroundColor(selectedTab == screen ? .blue : .gray)\n                .onTapGesture {\n                    withAnimation {\n                        selectedTab = screen\n                    }\n                }\n\n                Spacer()\n            }\n        }\n        .padding(.vertical, 8)\n        .background(Color(.systemGray6))\n    }\n}\n\n\/\/ \uc2a4\ud06c\ub9b0 \/ Screens\nstruct HomeView: View {\n    var body: some View {\n        VStack {\n            Spacer()\n            Text(\"\ud648 \ud654\uba74\uc785\ub2c8\ub2e4. \/ Home Screen\")\n            Spacer()\n        }\n    }\n}\n\nstruct SearchView: View {\n    var body: some View {\n        VStack {\n            Spacer()\n            Text(\"\uac80\uc0c9 \ud654\uba74\uc785\ub2c8\ub2e4. \/ Search Screen\")\n            Spacer()\n        }\n    }\n}\n\nstruct SettingsView: View {\n    var body: some View {\n        VStack {\n            Spacer()\n            Text(\"\uc124\uc815 \ud654\uba74\uc785\ub2c8\ub2e4.\/ Settings Screen\")\n            Spacer()\n        }\n    }\n}\n\n\n#Preview {\n    ContentView()\n}\n<\/code><\/pre>\n\n\n\n<p>\u2714\ufe0fScreenShot<br><a href=\"https:\/\/youtu.be\/MSctu4bMl3I?si=TUHMjG9wdc1UiNTL\">https:\/\/youtu.be\/MSctu4bMl3I?si=TUHMjG9wdc1UiNTL<\/a><\/p>\n\n\n\n<figure class=\"wp-block-embed is-type-video is-provider-youtube wp-block-embed-youtube wp-embed-aspect-16-9 wp-has-aspect-ratio\"><div class=\"wp-block-embed__wrapper\">\n<iframe loading=\"lazy\" title=\"[Kotlin,Swift]\ud558\ub2e8\ud0ed\uba54\ub274\/BottomTabMenu\" width=\"560\" height=\"315\" src=\"https:\/\/www.youtube.com\/embed\/MSctu4bMl3I?feature=oembed\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" referrerpolicy=\"strict-origin-when-cross-origin\" allowfullscreen><\/iframe>\n<\/div><\/figure>\n","protected":false},"excerpt":{"rendered":"<p>\ud83d\udc49\ud83c\udffb \ud558\ub2e8 \ud0ed \uba54\ub274,\ub4dc\ub86d\ub2e4\uc6b4 \uba54\ub274\uc5d0 \ub300\ud55c \uc124\uba85\uc785\ub2c8\ub2e4.Description of the bottom tab menu and drop-down menu. \ud83d\udc49\ud83c\udffb \uc2a4\ud06c\ub9b0 \ud30c\uc77c\uc774 \ub530\ub85c \ubd84\ub9ac\ub418\uc5b4\uc788\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4.The screen files are not separated separately. \ud83d\udc49\ud83c\udffb \ub098\uba38\uc9c0 \uc124\uba85\uc740 \ucf54\ub4dc \uc8fc\uc11d\uc744 \ucc38\uc870 \ud558\uc138\uc694Please refer to the code comments for the rest of the explanation. 1.\ucf54\ub4dc\/Code \u2714\ufe0f ContentView.swift \u2714\ufe0fScreenShothttps:\/\/youtu.be\/MSctu4bMl3I?si=TUHMjG9wdc1UiNTL<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[19,1],"tags":[],"class_list":["post-2785","post","type-post","status-publish","format-standard","hentry","category-swift","category-uncategorized","missing-thumbnail"],"_links":{"self":[{"href":"https:\/\/www.freelifemakers.org\/wordpress\/index.php\/wp-json\/wp\/v2\/posts\/2785","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.freelifemakers.org\/wordpress\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.freelifemakers.org\/wordpress\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.freelifemakers.org\/wordpress\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.freelifemakers.org\/wordpress\/index.php\/wp-json\/wp\/v2\/comments?post=2785"}],"version-history":[{"count":6,"href":"https:\/\/www.freelifemakers.org\/wordpress\/index.php\/wp-json\/wp\/v2\/posts\/2785\/revisions"}],"predecessor-version":[{"id":2810,"href":"https:\/\/www.freelifemakers.org\/wordpress\/index.php\/wp-json\/wp\/v2\/posts\/2785\/revisions\/2810"}],"wp:attachment":[{"href":"https:\/\/www.freelifemakers.org\/wordpress\/index.php\/wp-json\/wp\/v2\/media?parent=2785"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.freelifemakers.org\/wordpress\/index.php\/wp-json\/wp\/v2\/categories?post=2785"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.freelifemakers.org\/wordpress\/index.php\/wp-json\/wp\/v2\/tags?post=2785"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}