携手创作,共同生长!这是我参与「日新方案 8 月更文应战」的第24天,点击检查活动详情。
接受上一章节的内容,在上一章节中,咱们完结了“新建发布”的入口和背景蒙层的搭建,那么本章来进入要点,咱们来完结弹窗的交互。
款式预览
弹窗视图
咱们来剖析下“新建发布”弹窗的内容,它包含一个提示的下拉条,一排横向布局的发布功用按钮,一个封闭弹窗的按钮。
咱们构建一个新的视图,示例:
// MARK: 底部弹窗
struct SlideOutMenu: View {
@Binding var showMaskView: Bool
var body: some View {
VStack {
Spacer()
//构建弹窗视图元素
}
}
}
上述代码中,咱们创立了一个新视图SlideOutMenu
。以及还运用@Binding
声明晰一个变量,便利咱们在ContentView
视图中做双向绑定。由于弹窗在底部,咱们运用VStack
纵向布局,然后运用Spacer
将弹窗撑开到底部。
完结后,咱们来完结弹窗的款式部分。
下拉条
咱们一块一块内容完结它,首先是下拉条,示例:
// 下拉条
func pullDownBtnView() -> some View {
Rectangle()
.foregroundColor(Color(.systemGray4))
.cornerRadius(30)
.frame(width: 50, height: 5)
}
上述代码中,咱们构建了一个Rectangle
矩形,并赋予了颜色systemGray4
灰色和圆角,尺寸咱们运用规则的长宽。
发布功用按钮
发布功用按钮部分,由于具有相同的款式,咱们能够运用结构体的办法构建根底款式,再在视图中调用,也能够运用创立视图的办法构建根底款式再调用。两种办法都能够运用,示例:
// 操作功用
func operateBtnView(image: String, text: String) -> some View {
Button(action: {
self.showMaskView = false
}) {
VStack(spacing: 15) {
Image(systemName: image)
.font(.system(size: 30))
.foregroundColor(.black)
.frame(width: 80, height: 80)
.background(Color(.systemGray6))
.cornerRadius(8)
Text(text)
.font(.system(size: 17))
.foregroundColor(.black)
}
}
}
上述代码中,咱们构建了一个结构视图operateBtnView
,传入两个String
类型的变量image
、text
,别离代表操作按钮中的图标图片和操作按钮名称。
当咱们点击按钮的时分,切换showMaskView
状况封闭蒙层。
封闭按钮
封闭按钮款式也比较简单,咱们依旧独自构建款式部分,示例:
// 封闭按钮
func colseBtnView() -> some View {
Button(action: {
self.showMaskView = false
}) {
Image(systemName: "xmark")
.font(.system(size: 24))
.foregroundColor(.gray)
.padding(.bottom, 20)
}
}
上述代码中,咱们独自构建按款式视图colseBtnView
。当咱们点击封闭按钮的时分,也调用切换showMaskView
状况封闭蒙层。
款式组合
完结上述3个视图后,咱们在SlideOutMenu
视图中组合款式视图内容,示例:
// MARK: 底部弹窗
struct SlideOutMenu: View {
@Binding var showMaskView: Bool
var body: some View {
VStack {
Spacer()
//构建弹窗视图元素
VStack {
// 下拉条
pullDownBtnView()
Spacer()
// 操作按钮
HStack(spacing: 20) {
operateBtnView(image: "magazine.fill", text: "写文章")
operateBtnView(image: "doc.plaintext.fill", text: "发沸点")
operateBtnView(image: "book.fill", text: "提问题")
operateBtnView(image: "paperplane.fill", text: "传资源")
}
Spacer()
// 封闭按钮
colseBtnView()
}
.padding()
.frame(maxWidth: .infinity, maxHeight: 320)
.background(Color.white)
.cornerRadius(10, antialiased: true)
}.edgesIgnoringSafeArea(.bottom)
}
}
交互动画
弹出封闭
完结了弹窗视图后,咱们回到ContentView
视图中,将弹窗视图附上,示例:
var body: some View {
ZStack {
VStack {
topBarMenu()
Spacer()
}
if showMaskView {
MaskView(showMaskView: $showMaskView)
SlideOutMenu(showMaskView: $showMaskView)
.transition(.move(edge: .bottom))
.animation(.interpolatingSpring(stiffness: 200.0, damping: 25.0, initialVelocity: 10.0))
}
}
}
上述代码中,咱们依据showMaskView
变量状况决定是否展现背景蒙层视图和弹窗视图,然后在展现SlideOutMenu
新建发布弹窗时,运用transition
过渡和animation
动画加了一个从下向上展现的过渡动画。
向下拖动封闭
新建发布弹窗除了惯例的点击封闭按钮封闭弹窗外,点击弹窗向下拖动时封闭弹窗,要实现这个功用,咱们回到SlideOutMenu
弹窗视图中,首要声明2个变量,示例:
@State private var offsetY = CGSize.zero
@State var isAllowToDrag: Bool = false
上述代码中,offsetY
变量存储拖动时弹窗Y轴的方位,用来判别用户在向上拖动还是向下拖动,也为了确认向下拖动Y轴到某一方位的,触发封闭弹窗交互。
变量isAllowToDrag
是接受offsetY
变量,当咱们判别向上拖动时,禁用弹窗拖动,防止弹窗向上拖动,确保只能向下拖动。
然后在SlideOutMenu
主要内容中运用拖动修饰符,示例:
// MARK: 底部弹窗
struct SlideOutMenu: View {
@Binding var showMaskView: Bool
var body: some View {
VStack {
//躲藏了弹窗视图代码
}
.padding()
.frame(maxWidth: .infinity, maxHeight: 320)
.background(Color.white)
.cornerRadius(10, antialiased: true)
.offset(y: isAllowToDrag ? offsetY.height : 0)
.gesture(
DragGesture()
.onChanged { gesture in
// 假如向下拖动
if gesture.translation.height > 0 {
self.isAllowToDrag = true
self.offsetY = gesture.translation
}
}
.onEnded { _ in
// 假如拖动方位大于100
if (self.offsetY.height) > 100 {
self.showMaskView = false
} else {
self.offsetY = .zero
}
}
)
}.edgesIgnoringSafeArea(.bottom)
}
}
上述代码中,咱们给弹窗内容加了offset
偏移量修饰符,拖动时,假如isAllowToDrag
答应拖动,则拖动方位为offsetY.height
偏移的Y轴方位,否则便是0。
然后运用gesture
手势修饰符,运用DragGesture
拖着手势,当onChanged
拖动改动时,先判别是不是向下拖动,假如是则启用isAllowToDrag
变量,然后拖动后让视图回到本来的方位。
当弹窗视图onEnded
拖动结束时,判别拖动的Y轴的方位offsetY.height
是不是大于100
,也便是弹窗宽度的大约1/3
的方位,假如时则修改showMaskView
变量封闭弹窗。
项目预览
完结全部后,咱们全体预览下作用。
恭喜你,完结了本章的全部内容!
快来着手试试吧。
假如本专栏对你有帮助,不妨点赞、谈论、关注~