Contents 非表示
実装
今回使用&新規作成するファイル一覧
SideMenuRowType.swift
MainTabView.swift
SideMenuView.swift
SideMenu.swift
HomeView.swift
(デフォルトだとContentView.swift
)PostView.swift
NewsView.swift
SettingView.swift
TabItemView.swift
それでは作っていきましょう。
SideMenuRowType (各メニューアイテムの名前、アイコン画像の管理)
import Foundation
enum SideMenuRowType: Int, CaseIterable {
case home = 0
case post
case news
case setting
var title: String {
switch self {
case .home:
return "Home"
case .post:
return "Post"
case .news:
return "News"
case .setting:
return "Setting"
}
}
var iconName: String {
switch self {
case .home:
return "house"
case .post:
return "camera.viewfinder"
case .news:
return "newspaper"
case .setting:
return "gear"
}
}
}
MainTabView (サイドメニュー全体の管理)
import SwiftUI
struct MainTabView: View {
@State var presentSideMenu = false
@State var selectedSideMenuTab = 0
var body: some View {
ZStack {
TabView(selection: $selectedSideMenuTab) {
HomeView(presentSideMenu: $presentSideMenu)
.tag(0)
PostView(presentSideMenu: $presentSideMenu)
.tag(1)
NewsView(presentSideMenu: $presentSideMenu)
.tag(2)
SettingView(presentSideMenu: $presentSideMenu)
.tag(3)
}
SideMenu(isShowing: $presentSideMenu, content: AnyView(SideMenuView(selectedSideMenuTab: $selectedSideMenuTab, presentSideMenu: $presentSideMenu)))
}
}
}
struct MainTabView_Previews: PreviewProvider {
static var previews: some View {
MainTabView()
}
}
SideMenuView (サイドメニューのUIを構成)
import SwiftUI
struct SideMenuView: View {
@Binding var selectedSideMenuTab: Int
@Binding var presentSideMenu: Bool
var body: some View {
HStack {
ZStack {
Rectangle()
.fill(.white)
.frame(width: 270)
.shadow(color: .blue.opacity(0.3), radius: 5, x: 0, y: 3)
VStack(alignment: .leading, spacing: 0) {
profileImageView()
.frame(height: 140)
.padding(.bottom, 30)
ForEach(SideMenuRowType.allCases, id: \.self) { row in
rowView(isSelected: selectedSideMenuTab == row.rawValue, imageName: row.iconName, title: row.title) {
selectedSideMenuTab = row.rawValue
presentSideMenu.toggle()
}
}
Spacer()
}
.padding(.top, 100)
.frame(width: 270)
.background(
Color.white
)
}
Spacer()
}
.background(.clear)
}
@ViewBuilder
private func profileImageView() -> some View {
VStack(alignment: .center) {
HStack {
Spacer()
Image("<#好きな画像#>")
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: 100, height: 100)
.overlay(
RoundedRectangle(cornerRadius: 50)
.stroke(.blue.opacity(0.3), lineWidth: 10)
)
.cornerRadius(50)
Spacer()
}
Text("<#好きなテキスト#>")
.font(.system(size: 18, weight: .bold))
.foregroundColor(.black)
Text("<#好きなテキスト#>")
.font(.system(size: 14, weight: .semibold))
.foregroundColor(.black.opacity(0.5))
}
}
@ViewBuilder
private func rowView(isSelected: Bool, imageName: String, title: String, hideDivider: Bool = false, action: @escaping (() -> Void)) -> some View {
Button {
action()
} label: {
VStack(alignment: .leading) {
HStack(spacing: 20) {
Rectangle()
.fill(isSelected ? .blue.opacity(0.3) : .white)
.frame(width: 5)
ZStack {
Image(systemName: imageName)
.resizable()
.renderingMode(.template)
.foregroundColor(isSelected ? .black : .gray)
.frame(width: 26, height: 26)
}
.frame(width: 30, height: 30)
Text(title)
.font(.system(size: 14, weight: .regular))
.foregroundColor(isSelected ? .black : .gray)
Spacer()
}
}
}
.frame(height: 50)
.background(
LinearGradient(colors: [isSelected ? .blue.opacity(0.3) : .white, .white], startPoint: .leading, endPoint: .trailing)
)
}
}
struct SideMenuView_Previews: PreviewProvider {
static var previews: some View {
SideMenuView(selectedSideMenuTab: .constant(0), presentSideMenu: .constant(false))
}
}
SideMenu
import SwiftUI
struct SideMenu: View {
@Binding var isShowing: Bool
var content: AnyView
var edgeTransition: AnyTransition = .move(edge: .leading)
var body: some View {
ZStack(alignment: .bottom) {
if isShowing {
Color.black
.opacity(0.3)
.ignoresSafeArea()
.onTapGesture {
isShowing.toggle()
}
content
.transition(edgeTransition)
.background(
Color.clear
)
}
}
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .bottom)
.ignoresSafeArea()
.animation(.easeInOut, value: isShowing)
}
}
struct SideMenu_Previews: PreviewProvider {
static var previews: some View {
SideMenu(isShowing: .constant(false), content: AnyView(MainTabView()))
}
}
HomeView ~ SettingView (各メニューアイテムのView)
import SwiftUI
struct <#各Viewの名前#>: View {
@Binding var presentSideMenu: Bool
var body: some View {
TabItemView(presentSideMenu: $presentSideMenu, title: "<#各Viewの名前#>")
.padding(.horizontal, 24)
}
}
struct <#各Viewの名前#>_Previews: PreviewProvider {
static var previews: some View {
<#各Viewの名前#>(presentSideMenu: .constant(false))
}
}
TabItemView (各メニューのUIをまとめる)
import SwiftUI
/// 各タブアイテムのView
struct TabItemView: View {
@Binding var presentSideMenu: Bool
let title: String
var body: some View {
VStack {
HStack {
Button {
presentSideMenu.toggle()
} label: {
Image(systemName: "list.bullet")
.resizable()
.frame(width: 32, height: 32)
.foregroundColor(.black)
}
Spacer()
}
Spacer()
Text(title)
.font(.system(size: 32, weight: .medium))
Spacer()
}
}
}
(プロジェクト名).swift (初期表示するViewを決めるところ)
import SwiftUI
@main
struct SwiftUI_PlaygroundApp: App {
@State static var presentSideMenu = false
var body: some Scene {
WindowGroup {
MainTabView()
}
}
}