การทำ Animated Curved Tabbar จะคล้ายๆกับการทำ BottomNavigationBar ซึ่งจะมีลักษณะเป็นแถบด้านล่างให้เหมือนกัน แต่อนิเมชั่นการเล่นการแสดงผลนั้นจะแตกต่างกันไป เด่วเราไปดูวิธีการทำ Animated Curved Tabbar เลย
อธิบายโค้ด
HStack(spacing: 0){
TabBarButton(image: "house", selectedTab: $selectedTab, tabPoints: $tabPoints)
TabBarButton(image: "bookmark", selectedTab: $selectedTab, tabPoints: $tabPoints)
TabBarButton(image: "location", selectedTab: $selectedTab, tabPoints: $tabPoints)
TabBarButton(image: "person", selectedTab: $selectedTab, tabPoints: $tabPoints)
}
.padding()
.background(
Color.white
.clipShape(TabCurve(tabPoint: getCurvePoint() - 15))
)
.overlay(
Circle()
.fill(Color.white)
.frame(width: 10, height: 10)
.offset(x: getCurvePoint() - 20)
,alignment: .bottomLeading
)
.cornerRadius(30)
.padding(.horizontal)
}
สำหรับการทำในตัวอย่างนี้ จะมีการใส่ TabBarButton ลงใน HStack ก่อนเพื่อให้มีการจัดเรียงกันของปุ่มในแนวนอนทั้งหมด 4 ปุ่ม และมีการกำหนด image ภายในปุ่มเป็นลักษณะ house , bookmark , location , person เพื่อให้แสดงไอคอนแตกต่างกันออกไป
ส่วนตรง Circle ก็จะเป็นการกำหนดวงกลมสีขาวไว้ที่ด้านล่างของแต่ละปุ่ม
func getCurvePoint()->CGFloat{
if tabPoints.isEmpty{
return 10
}
else{
switch selectedTab {
case "house":
return tabPoints[0]
case "bookmark":
return tabPoints[1]
case "location":
return tabPoints[2]
default:
return tabPoints[3]
}
}
}
ฟังก์ชั่น getCurvePoint จะเป็นฟังก์ชั่นเอาไว้กำหนดว่าถ้าเลือกปุ่มใด จะแสดงข้อมูลของปุ่มนั้นๆ เช่น กดปุ่ม house จะ return ค่า tabPoints[0] เพื่อนำไปใช้ต่อ
Button(action: {
withAnimation(.interactiveSpring(response: 0.6, dampingFraction: 0.5, blendDuration: 0.5)){
selectedTab = image
}
}, label: {
Image(systemName: "\(image)\(selectedTab == image ? ".fill" : "")")
.font(.system(size: 25, weight: .semibold))
.foregroundColor(Color("TabSelected"))
.offset(y: selectedTab == image ? -10 : 0)
})
.frame(maxWidth: .infinity, maxHeight: .infinity)
.contentShape(Rectangle())
ส่วนสำคัญอีกส่วนคือการกำหนดเงื่อนไขของปุ่ม ว่ากดแล้วให้ทำอะไรต่อ โดยโค้ดส่วนนี้คือเมื่อกดแล้วจะแสดงอนิเมชั่น และจะทำการเปลี่ยนสีรูปภาพเป็นสี "TabSelected" (สีม่วง) ที่เราได้กำหนดเอาไว้
โค้ดทั้งหมด
ไฟล์ Home.swift
import SwiftUI
struct Home: View {
@State var selectedTab = "house"
var body: some View {
ZStack(alignment: .bottom, content: {
Color("TabBG").ignoresSafeArea()
CustomTabBar(selectedTab: $selectedTab)
})
}
}
struct Home_Previews: PreviewProvider {
static var previews: some View {
Home()
}
}
ไฟล์ CustomTabBar.swift
import SwiftUI
struct CustomTabBar: View {
@Binding var selectedTab: String
@State var tabPoints : [CGFloat] = []
var body: some View {
HStack(spacing: 0){
TabBarButton(image: "house", selectedTab: $selectedTab, tabPoints: $tabPoints)
TabBarButton(image: "bookmark", selectedTab: $selectedTab, tabPoints: $tabPoints)
TabBarButton(image: "location", selectedTab: $selectedTab, tabPoints: $tabPoints)
TabBarButton(image: "person", selectedTab: $selectedTab, tabPoints: $tabPoints)
}
.padding()
.background(
Color.white
.clipShape(TabCurve(tabPoint: getCurvePoint() - 15))
)
.overlay(
Circle()
.fill(Color.white)
.frame(width: 10, height: 10)
.offset(x: getCurvePoint() - 20)
,alignment: .bottomLeading
)
.cornerRadius(30)
.padding(.horizontal)
}
func getCurvePoint()->CGFloat{
if tabPoints.isEmpty{
return 10
}
else{
switch selectedTab {
case "house":
return tabPoints[0]
case "bookmark":
return tabPoints[1]
case "location":
return tabPoints[2]
default:
return tabPoints[3]
}
}
}
}
struct CustomTabBar_Previews: PreviewProvider {
static var previews: some View {
Home()
}
}
struct TabBarButton: View {
var image: String
@Binding var selectedTab: String
@Binding var tabPoints: [CGFloat]
var body: some View{
GeometryReader{reader -> AnyView in
let midX = reader.frame(in: .global).midX
DispatchQueue.main.async {
if tabPoints.count <= 4{
tabPoints.append(midX)
}
}
return AnyView(
Button(action: {
print(selectedTab)
withAnimation(.interactiveSpring(response: 0.6, dampingFraction: 0.5, blendDuration: 0.5)){
selectedTab = image
}
}, label: {
Image(systemName: "\(image)\(selectedTab == image ? ".fill" : "")")
.font(.system(size: 25, weight: .semibold))
.foregroundColor(Color("TabSelected"))
.offset(y: selectedTab == image ? -10 : 0)
})
.frame(maxWidth: .infinity, maxHeight: .infinity)
.contentShape(Rectangle())
)
}
.frame(height: 50)
}
}
ไฟล์ TabCurve.swift
import SwiftUI
struct TabCurve: Shape {
var tabPoint: CGFloat
// animating path...
var animatableData: CGFloat{
get{return tabPoint}
set{tabPoint = newValue}
}
func path(in rect: CGRect) -> Path {
return Path{path in
// Drawing Curve Path.....
path.move(to: CGPoint(x: rect.width, y: rect.height))
path.addLine(to: CGPoint(x: rect.width, y: 0))
path.addLine(to: CGPoint(x: 0, y: 0))
path.addLine(to: CGPoint(x: 0, y: rect.height))
let mid = tabPoint
path.move(to: CGPoint(x: mid - 40, y: rect.height))
let to = CGPoint(x: mid, y: rect.height - 20)
let contorl1 = CGPoint(x: mid - 15, y: rect.height)
let control2 = CGPoint(x: mid - 15, y: rect.height - 20)
let to1 = CGPoint(x: mid + 40, y: rect.height)
let contorl3 = CGPoint(x: mid + 15, y: rect.height - 20)
let control4 = CGPoint(x: mid + 15, y: rect.height)
path.addCurve(to: to, control1: contorl1, control2: control2)
path.addCurve(to: to1, control1: contorl3, control2: control4)
}
}
}
struct TabCurve_Previews: PreviewProvider {
static var previews: some View {
Home()
}
}
ไม่ยากเลยใช่มั้ยครับสำหรับการทำ Animation Tab Bar สวยๆแบบนี้ โค้ดในภาษา SwiftUI ถ้ายังไม่คุ้นชินอาจจะดูงงอยู่บ้าง แต่โดยรวมแล้วก็ยังมีความคล้ายภาษาอื่นอยู่
Credit By : https://www.youtube.com/watch?v=TJfI3-qdta8
Source Code : https://github.com/tanat29/SwiftUI-AnimatedCurvedTabbar
ความคิดเห็น