4

Im trying to get midX position of Button, but it always gives me unexpected result. I have tried to use .global, .local. and .named coordinate spaces but it still don't work. Maybe there is another way to get coordinates of UI element without GeometryReader The curve center should be in the center of selected button.

enter image description here

struct CoreTabBar: View {
@State var selectedTab: Tab = .home
@State private var xAxis: CGFloat = 0

private let home: AnyView
private let search: AnyView
private let media: AnyView

init(
    home: AnyView,
    search: AnyView,
    media: AnyView
) {
    self.home = home
    self.search = search
    self.media = media
}

var body: some View {
    ZStack(alignment: .center) {
        TabView(selection: $selectedTab) {
            switch selectedTab {
            case .home:
                home
                    .ignoresSafeArea()
            case .search:
                search
                    .ignoresSafeArea()
            case .media:
                media
                    .ignoresSafeArea()
            }
        }
        VStack {
            Spacer()
            HStack(spacing: 0) {
                ForEach(Tab.allCases, id: \.self) { tab in
                    if tab == .search { Spacer(minLength: 0) }
                    
                    GeometryReader { proxy in
                        Button {
                            selectedTab = tab
                            xAxis = proxy.frame(in: .named("TabBar")).midX
                        } label: {
                            Image(systemName: tabImage(tab))
                                .resizable()
                                .renderingMode(.template)
                                .aspectRatio(contentMode: .fit)
                                .frame(width: 25, height: 25)
                                .foregroundColor(tab == selectedTab ? Color.blue : Color.gray)
                                .coordinateSpace(name: "Button")
                        }
                        
                        .onAppear {
                        }
                    }
                    .frame(width: 25, height: 25)
            
                    if tab == .search { Spacer(minLength: 0) }
                }
            }
            .coordinateSpace(name: "TabBar")
            .padding(.horizontal, 60)
            .padding(.vertical)
            .background(Color.white.clipShape(TabBarShape(xAxis: self.xAxis)))
            .cornerRadius(50)
            .padding(.horizontal, idiomIsPhone() ? nil : tabBarHorizontalPaddingForIpad())
            Spacer()
                .frame(height: 5)
        }
    }
}

1 Answers1

2

It seems to work just fine with .global coordinates. See my demo below. Maybe there is a problem with the use of xAxis in TabBarShape ... can't tell because you didn't share it.

enter image description here

import SwiftUI

enum Tab: CaseIterable {
    case home
    case search
    case media
}

struct ContentView: View {
    @State var selectedTab: Tab = .home
    @State private var xAxis: CGFloat = 0
    
    private let home = Text("home")
    private let search = Text("search")
    private let media = Text("media")
        
    var body: some View {
        ZStack(alignment: .center) {
            TabView(selection: $selectedTab) {
                switch selectedTab {
                case .home:
                    home
                        .ignoresSafeArea()
                case .search:
                    search
                        .ignoresSafeArea()
                case .media:
                    media
                        .ignoresSafeArea()
                }
            }
            VStack {
                Spacer()
                HStack(spacing: 0) {
                    ForEach(Tab.allCases, id: \.self) { tab in
                        if tab == .search { Spacer(minLength: 0) }
                        
                        GeometryReader { proxy in
                            Button {
                                withAnimation {
                                    selectedTab = tab
                                    xAxis = proxy.frame(in: .global).midX // .global should work fine
                                }
                            } label: {
                                Image(systemName: "house")
                                    .resizable()
                                    .renderingMode(.template)
                                    .aspectRatio(contentMode: .fit)
                                    .frame(width: 25, height: 25)
                                    .foregroundColor(tab == selectedTab ? Color.blue : Color.gray)
                            }
                            .onAppear {
                                if tab == selectedTab {
                                    xAxis = proxy.frame(in: .global).midX // set for initial selection
                                }
                            }
                        }
                        .frame(width: 25, height: 25)
                        
                        if tab == .search { Spacer(minLength: 0) }
                    }
                }
                .padding(.horizontal, 60)
                .padding(.vertical)
                .background(
                    ZStack {
                        Color.green
                    Circle().fill(.white).frame(width: 50)
                        .position(x: xAxis) // applied here
                    }
                    )
                .cornerRadius(50)
//                .padding(.horizontal, idiomIsPhone() ? nil : tabBarHorizontalPaddingForIpad())
                Spacer()
                    .frame(height: 5)
            }
        }
    }
}
ChrisR
  • 9,523
  • 1
  • 8
  • 26