TabBar Customisation in SwiftUI

368 views Asked by At

I want to customize the tab bar like the curved rectangle in the center but all i am able to do is added one image in the center. the border should come below the circle, tried so many ways but it didn't worked, hope someone would help me to get this.

Here I have tried:

enter image description here

What I am expecting:

enter image description here

TabBarView:

struct TabBarView: View {
    
    @State var selectedTab = 0
    
    var body: some View {
        ZStack(alignment: .bottom) {
                    TabView(selection: $selectedTab) {
                        HomeView()
                            .tag(0)

                        Search()
                            .tag(1)

                        Tickets()
                            .tag(2)

                        Profile()
                            .tag(3)

                        Settings()
                            .tag(4)
                    }

                    RoundedRectangle(cornerRadius: 25)
                        .frame(width: 350, height: 70)
                        .foregroundColor(.white)
                        .shadow(radius: 0.8)

                    Button {
                        selectedTab = 2
                    } label: {
                        CustomTabItem(imageName: "ticket", title: "Ticket", isActive: (selectedTab == 2))
                    }
                    .frame(width: 65, height: 65)
                    .background(Color.white)
                    .clipShape(Circle())
                    .shadow(radius: 0.8)
                    .offset(y: -50)

                    HStack {
                        ForEach(TabbedItems.allCases, id: \.self) { item in
                            if item != .ticket { // Exclude the center button
                                Button {
                                    selectedTab = item.rawValue
                                } label: {
                                    CustomTabItem(imageName: item.iconName, title: item.title, isActive: (selectedTab == item.rawValue))
                                }
                            }
                        }
                    }
                    .frame(height: 70)
                }
            }
        }

Extension:

extension TabBarView {
    func CustomTabItem(imageName: String, title: String, isActive: Bool) -> some View{
        HStack(alignment: .center,spacing: 22){
            Spacer()
            Image(imageName)
                .resizable()
                .renderingMode(.template)
                .foregroundColor(isActive ? .purple : .gray)
                .frame(width: 25, height: 25)
            Spacer()
        }
    }
}
1

There are 1 answers

1
Benzy Neez On BEST ANSWER

You can do it by creating a Shape with the required form. I had a go, here's something that's close:

struct TabBarShape: Shape {
    let insetRadius: CGFloat
    let cornerRadius = CGFloat(25)
    let insetCornerAngle = 45.0

    func path(in rect: CGRect) -> Path {
        var path = Path()

        // Start just below the top-left corner
        var x = rect.minX
        var y = rect.minY + cornerRadius
        path.move(to: CGPoint(x: x, y: y))

        // Add the rounded corner on the top-left corner
        x += cornerRadius
        path.addArc(
            center: CGPoint(x: x, y: y),
            radius: cornerRadius,
            startAngle: .degrees(180),
            endAngle: .degrees(270),
            clockwise: false
        )
        // Begin inset in middle, cutting into shape
        x = rect.midX - (2 * insetRadius)
        y = rect.minY + insetRadius
        path.addArc(
            center: CGPoint(x: x, y: y),
            radius: insetRadius,
            startAngle: .degrees(270),
            endAngle: .degrees(270 + insetCornerAngle),
            clockwise: false
        )
        // Add a half-circle to fit the button
        x = rect.midX
        y = rect.minY
        path.addArc(
            center: CGPoint(x: x, y: y),
            radius: insetRadius,
            startAngle: .degrees(90 + insetCornerAngle),
            endAngle: .degrees(90 - insetCornerAngle),
            clockwise: true
        )
        // Complete the inset with the second rounded corner
        x += (2 * insetRadius)
        y += insetRadius
        path.addArc(
            center: CGPoint(x: x, y: y),
            radius: insetRadius,
            startAngle: .degrees(270 - insetCornerAngle),
            endAngle: .degrees(270),
            clockwise: false
        )
        // Top-right corner
        x = rect.maxX - cornerRadius
        y = rect.minY + cornerRadius
        path.addArc(
            center: CGPoint(x: x, y: y),
            radius: cornerRadius,
            startAngle: .degrees(270),
            endAngle: .degrees(0),
            clockwise: false
        )
        // Bottom-right corner
        y = rect.maxY - cornerRadius
        path.addArc(
            center: CGPoint(x: x, y: y),
            radius: cornerRadius,
            startAngle: .degrees(0),
            endAngle: .degrees(90),
            clockwise: false
        )
        // Bottom-left corner
        x = rect.minX + cornerRadius
        path.addArc(
            center: CGPoint(x: x, y: y),
            radius: cornerRadius,
            startAngle: .degrees(90),
            endAngle: .degrees(180),
            clockwise: false
        )
        path.closeSubpath()
        return path
    }
}

Then you can use the custom shape in place of the RoundedRectangle that you were using before:

// RoundedRectangle(cornerRadius: 25)
TabBarShape(insetRadius: 30)
    .frame(width: 350, height: 70)
    .foregroundColor(.white)
    .shadow(color: Color(white: 0.8), radius: 6, x: 0, y: 3)

Button {
    selectedTab = 2
} label: {
    CustomTabItem(imageName: "ticket", title: "Ticket", isActive: (selectedTab == 2))
}
.frame(width: 50, height: 50)
.background(Color.white)
.clipShape(Circle())
.shadow(radius: 0.8)
.offset(y: -50)

Here's how it looks, you'll probably want to tweak the inset a bit more:

TabBarShape