Home > database >  matchedGeometryEffect alternative for iOS 14
matchedGeometryEffect alternative for iOS 14

Time:07-30

I have a tab bar that works great on iOS 15.x, however on iOS 14 it fails miserably. Anyone know of an alternative method I can use?

iOS 14 iOS 15

public struct Tab: Identifiable, Equatable, Hashable {
    public let title: String
    public let isEnabled: Bool
    public let id: Int

    var isDisabled: Bool {
        !isEnabled
    }

    public init(title: String, isEnabled: Bool = true, id: Int) {
        self.title = title
        self.isEnabled = isEnabled
        self.id = id
    }

    public static func == (lhs: Tab, rhs: Tab) -> Bool {
      lhs.id == rhs.id
    }
}

public struct TabBar: View {
    private let tabs: [Tab]
    @Binding private var selectedTab: Tab
    @State private var selectedIndex = 0

    @Namespace var namespace

    public init(tabs: [Tab], selectedTab: Binding<Tab>) {
        self.tabs = tabs
        self._selectedTab = selectedTab
    }

    public var body: some View {
        ScrollView(.horizontal, showsIndicators: false) {
            HStack(spacing: 0) {
                ForEach(Array(zip(self.tabs.indices, self.tabs)), id: \.0) { index, tab in
                    TabBarItem(selectedIndex: self.$selectedIndex,
                               selectedTab: self.$selectedTab,
                               namespace: namespace.self,
                               tab: tab,
                               index: index)
                }
            }
            .padding(.horizontal)
        }
        .onAppear {
            selectedIndex = tabs.firstIndex(where: { $0.id == selectedTab.id }) ?? 0
        }
    }
}

struct TabBarItem: View {
    @Binding var selectedIndex: Int
    @Binding var selectedTab: Tab
    let namespace: Namespace.ID
    let tab: Tab
    let index: Int

    var isSelected: Bool {
        selectedIndex == index
    }

    var textColor: Color {
        if tab.isEnabled {
            return Color.black
        } else {
            return Color.gray
        }
    }

    var body: some View {
        Button {
            selectedIndex = index
            selectedTab = tab
        } label: {
            VStack(spacing: 0) {
                Text(tab.title)
                    .font(.body)
                    .padding(.horizontal, 4)
                    .foregroundColor(isSelected ? Color.blue : Color.black)

                if isSelected{
                    Color.red
                        .frame(height: 2)
                        .padding(.top, 2)
                        .matchedGeometryEffect(id: "underline", in: namespace, properties: .frame)
                } else {
                    Color.clear.frame(height: 2)
                }
            }
            .animation(.spring(), value: selectedIndex)
        }
        .disabled(tab.isDisabled)
        .buttonStyle(.plain)
    }
}

CodePudding user response:

The Color pushes VStack to take all available space, and to avoid this and fit to Text we can use the fixedSize modifier, like

        VStack(spacing: 0) {
            Text(tab.title)
                .font(.body)
                .padding(.horizontal, 4)
                .foregroundColor(isSelected ? Color.blue : Color.black)

            if isSelected{
                Color.red
                    .frame(height: 2)
                    .padding(.top, 2)
                    .matchedGeometryEffect(id: "underline", in: namespace, properties: .frame)
            } else {
                Color.clear.frame(height: 2)
            }
        }
        .fixedSize()      // << here !!
        //.fixedSize(horizontal: true, vertical: false)  // << or this
        .animation(.spring(), value: selectedIndex)

CodePudding user response:

I think every item on your VStack(spacing: 0) { } has same layout priority, and Color fill all spaces of your VStack. So please try to higher your tab.title Text layout priority.

Text(tab.title)
    .font(.body)
    .padding(.horizontal, 4)
    .layoutPriority(1)
    .foregroundColor(isSelected ? Color.blue : Color.black)
  • Related