Skip to content

Instantly share code, notes, and snippets.

@prafullakumar
Created August 15, 2020 13:23
Show Gist options
  • Save prafullakumar/c17f788445b37d0df689cf6c9378ee31 to your computer and use it in GitHub Desktop.
Save prafullakumar/c17f788445b37d0df689cf6c9378ee31 to your computer and use it in GitHub Desktop.
import SwiftUI
struct TabBarDemo: View {
@StateObject var tabItems = TabItems()
var body: some View {
ZStack {
///View1
NavigationView {
ZStack {
Color.gray
Text("Home")
}
.navigationBarTitle("Home")
}
.opacity((tabItems.selectedTabIndex == 1) ? 1 : 0)
///View2
NavigationView {
ZStack {
Color.gray
NavigationLink(destination: {
ZStack {
Color.gray
Text("Second Search View")
}.navigationBarTitle("Second View")
}()) {
Text("Search")
}
}
.navigationBarTitle("Search")
}
.opacity((tabItems.selectedTabIndex == 2) ? 1 : 0)
///View3
NavigationView {
ZStack {
Color.gray
Text("Add")
}
.navigationBarTitle("Add")
}
.opacity((tabItems.selectedTabIndex == 3) ? 1 : 0)
///View4
NavigationView {
ZStack {
Color.gray
Text("Favorite")
}
.navigationBarTitle("Favorite")
}
.opacity((tabItems.selectedTabIndex == 4) ? 1 : 0)
///View5
NavigationView {
ZStack {
Color.gray
Text("Profile")
}
.navigationBarTitle("Profile")
}
.opacity((tabItems.selectedTabIndex == 5) ? 1 : 0)
TabBar(tabItems: tabItems)
}
}
}
struct TabBar: View {
@ObservedObject var tabItems: TabItems
let padding: CGFloat = 5
let iconeSize: CGFloat = 20
var iconFrame: CGFloat {
(padding * 2) + iconeSize
}
var tabItemCount: CGFloat {
CGFloat(tabItems.items.count)
}
var spacing: CGFloat {
(UIScreen.main.bounds.width - (iconFrame * tabItemCount)) / (tabItemCount + 1)
}
var firstCenter: CGFloat {
spacing + iconFrame/2
}
var stepperToNextCenter: CGFloat {
spacing + iconFrame //half of 1 and half of next
}
var body: some View {
VStack {
Spacer()
ZStack {
Bar(tabItems: tabItems,
firstCenter: firstCenter,
stepperToNextCenter: stepperToNextCenter)
.foregroundColor(.white)
.frame(width: UIScreen.main.bounds.width, height: 50)
HStack(spacing: spacing) {
ForEach(0..<tabItems.items.count, id: \.self) { i in
ZStack {
Image(systemName: self.tabItems.items[i].imageName)
.resizable()
.foregroundColor(Color.gray)
.frame(width: self.iconeSize, height: self.iconeSize)
.opacity(self.tabItems.items[i].opacity)
.padding(.all, padding)
.background(Color.white)
.clipShape(Circle())
.onTapGesture {
withAnimation(Animation.easeInOut) {
self.tabItems.select(i)
}
}
}
.offset(y: self.tabItems.items[i].offset)
}
}
.edgesIgnoringSafeArea(.all)
}
}
}
}
struct Bar: Shape {
@ObservedObject var tabItems: TabItems
var tab: CGFloat
let firstCenter: CGFloat
let stepperToNextCenter: CGFloat
init(tabItems: TabItems, firstCenter: CGFloat, stepperToNextCenter: CGFloat) {
self.tabItems = tabItems
self.tab = tabItems.selectedTabIndex
self.firstCenter = firstCenter
self.stepperToNextCenter = stepperToNextCenter
}
var animatableData: Double {
get { return Double(tab) }
set { tab = CGFloat(newValue) }
}
func path(in rect: CGRect) -> Path {
let tabCenter = firstCenter + stepperToNextCenter * (tab - 1)
return Path { p in
p.move(to: CGPoint(x: rect.minX, y: rect.minY))
p.addLine(to: CGPoint(x: rect.minX, y: rect.maxY))
p.addLine(to: CGPoint(x: rect.maxX, y: rect.maxY))
p.addLine(to: CGPoint(x: rect.maxX, y: rect.minY))
p.addLine(to: CGPoint(x: tabCenter + 50, y: rect.minY))
p.addCurve(to: CGPoint(x: tabCenter, y: rect.midY),
control1: CGPoint(x: tabCenter + 20, y: rect.minY),
control2: CGPoint(x: tabCenter + 20, y: rect.minY + 25))
p.addCurve(to: CGPoint(x: tabCenter - 50, y: rect.minY),
control1: CGPoint(x: tabCenter - 20, y: rect.minY + 25),
control2: CGPoint(x: tabCenter - 20, y: rect.minY))
p.addLine(to: CGPoint(x: rect.maxX - tabCenter, y: rect.minY))
}
}
}
class TabItem: Identifiable {
let id = UUID()
let imageName: String
var offset: CGFloat = -5
var opacity: Double = 1
init(imageName: String, offset: CGFloat) {
self.imageName = imageName
self.offset = offset
}
init(imageName: String) {
self.imageName = imageName
}
}
class TabItems: ObservableObject {
@Published var items: [TabItem] = [
TabItem(imageName: "house", offset: -20),
TabItem(imageName: "magnifyingglass"),
TabItem(imageName: "plus.app"),
TabItem(imageName: "heart"),
TabItem(imageName: "person"),
]
@Published var selectedTabIndex: CGFloat = 1
func select(_ index: Int) {
let tabItem = items[index]
tabItem.opacity = 0
tabItem.offset = 15
withAnimation(Animation.easeInOut) {
selectedTabIndex = CGFloat(index + 1)
for i in 0..<items.count {
if i != index {
items[i].offset = -5
}
}
}
withAnimation(Animation.easeOut(duration: 0.25).delay(0.25)) {
tabItem.opacity = 1
tabItem.offset = -25
}
}
}
struct CustomTabBar_Previews: PreviewProvider {
static var previews: some View {
TabBarDemo()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment