SwiftUIでも上から下に向かうハーフモーダルの実装がなかったので、以下で実装
備忘録として残します。
使い方
SideMenuを呼び出して利用
機能概要
・表示のアニメーション
・ハーフモーダル表示中は後ろのViewの色のトーンを落とす
・後ろのViewタップ時は閉じる
実装
//ContentView.swift
import SwiftUI
struct ContentView: View {
@State private var isOpen:Bool = false
var body: some View {
ZStack() {
Button(action: {
isOpen.toggle()
}) {
Text("OpenSideMenu")
.frame(width: 200, height: 50, alignment: .center)
.background(Color.black)
.foregroundColor(.white)
.cornerRadius(5.0)
}
SideMenu(view: MenuView(), width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height/2, menuType: .fromTop,isOpen: $isOpen)
}.background(Color.green)
.ignoresSafeArea(.all)
}
}
//MenuView.swift
import SwiftUI
struct MenuView: View {
var body: some View {
VStack() {
Text("ハーフモーダル")
.font(.title)
.bold()
Image("myImg")
.resizable()
.clipShape(Circle())
.overlay(
Circle().stroke(Color.white, lineWidth: 2))
.frame(width: 50, height: 50, alignment: .leading)
}
}
}
//SideMenu.swift
import SwiftUI
//サイドメニュー
struct SideMenu<Content: View>: View {
enum menutype {
case fromLeft
case fromRight
case fromTop
case fromBottom
}
let view: Content //表示させるViewを指定
let width: CGFloat //メニューの幅を指定
let height:CGFloat //メニューの高さを指定
let menuType: menutype //type指定(.fromLeft, .fromRight, .fromTop)
@Binding var isOpen: Bool //BtnのBoolを引数とする
var body: some View {
ZStack {
GeometryReader { _ in
EmptyView()
}
.background(Color.gray.opacity(0.3))
.opacity(self.isOpen ? 1.0 : 0.0)
.animation(Animation.easeIn.delay(0.25))
.onTapGesture {
self.isOpen.toggle()
}
let displyView = view
.frame(width: self.width, height: self.height)
.background(Color.white)
.animation(.default)
switch self.menuType {
case .fromLeft:
HStack() {
displyView
.offset(x: self.isOpen ? 0 : -self.width)
Spacer()
}
case .fromRight:
HStack() {
Spacer()
displyView
.offset(x: self.isOpen ? 0 : +self.width)
}
case .fromTop:
VStack() {
displyView
.offset(y: self.isOpen ? 0 : -self.height)
Spacer()
}
case .fromBottom:
VStack() {
Spacer()
displyView
.offset(y: self.isOpen ? 0 : +self.height)
}
}
}.ignoresSafeArea(.all)
}
}