釋出&選擇釋出,使用SwiftUI搭建一個新建釋出彈窗(下)
theme: smartblue
攜手創作,共同成長!這是我參與「掘金日新計劃 · 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
變數關閉彈窗。
專案預覽
完成全部後,我們整體預覽下效果。
恭喜你,完成了本章的全部內容!
快來動手試試吧。
如果本專欄對你有幫助,不妨點贊、評論、關注~
- 實戰教程·元宇宙來了,準備好你的電子名片了嗎?(一)
- 實戰教程·元宇宙來了,準備好你的電子名片了嗎?(八)
- 實戰教程·什麼年代了還在敲傳統木魚?(二)
- 實戰教程·元宇宙來了,準備好你的電子名片了嗎?(七)
- 實戰教程·元宇宙來了,準備好你的電子名片了嗎?(六)
- 實戰教程·元宇宙來了,準備好你的電子名片了嗎?(五)
- 實戰教程·元宇宙來了,準備好你的電子名片了嗎?(四)
- 實戰教程·元宇宙來了,準備好你的電子名片了嗎?(三)
- 實戰教程·元宇宙來了,準備好你的電子名片了嗎?(二)
- 實戰教程·什麼年代了還在敲傳統木魚?(一)
- 技術下午茶:產品經理是如何工作的?如何才算一份好的需求文件?如何設計一個簡單的列表,它應該具備哪些基本功能?
- 釋出&選擇釋出,使用SwiftUI搭建一個新建釋出彈窗(上)
- 釋出&選擇釋出,使用SwiftUI搭建一個新建釋出彈窗(下)
- 使用SwiftUI搭建一個風箏搖擺動畫,實現放風箏的夢想~
- SwiftUI100天:使用SwiftUI搭建一個計時器App
- 實戰程式設計·使用SwiftUI從0到1完成一款iOS筆記App(三)
- 初識MVVM·關於啟動頁、引導頁、登入頁的設計細節和互動邏輯
- 誰說程式設計師不懂浪漫,教你使用SwiftUI搭建一個電子相簿送給她吧~
- 實戰程式設計·刻在男人DNA裡的浪漫,空氣投籃(二)
- 實戰程式設計·使用SwiftUI從0到1完成一款iOS筆記App(四)