Created
August 15, 2020 13:23
-
-
Save prafullakumar/c17f788445b37d0df689cf6c9378ee31 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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