การทำ Animated Curved Tabbar บน SwiftUI แบบเท่ห์ๆ

 

การทำ 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

ความคิดเห็น

โพสต์ยอดนิยมจากบล็อกนี้

การรับค่า ส่งค่าจากหน้าที่ 1 ไปหน้าที่ 2 บน Flutter (How to pass data between screens in Flutter?)

การจับเวลาบน Flutter ทำยังไง (How to count up timer flutter?)

วิธีการสลับข้อมูล MySQL ในคอลัมน์เดียวกัน