Danylo Horielov
Danylo Horielov

Reputation: 69

The navigation animation and UI break after implementing page blocking for TabView on iOS 18

I need to prevent users from selecting a different tab in TabView. The solution using onChange no longer works on iOS 18.

Steps to reproduce:

  1. Navigate from HomeDetails → (UI works as expected)
  2. Navigate from HomeDirectory (blocked by onChange) → Details (UI breaks)
  3. Navigate from HomeDirectory (blocked by onChange) → Details (UI breaks) -> Back (Now we can't navigate to Details anymore).
struct TabViewBugView: View {
  enum AppTab {
    case home
    case directory
  }
  
  @State private var selection: AppTab = .home
  
  var body: some View {
    TabView(selection: $selection) {
      NavigationStack {
        NavigationLink {
          VStack {
            Text("Details")
            Spacer()
          }
        } label: {
          Text("Go to Details")
        }
        
        Text("HomeView")
      }
      .tabItem {
        Label("Home", systemImage: "house")
      }
      .tag(AppTab.home)
      
      
      Text("Directory")
        .tabItem {
          Label("Directory", systemImage: "folder")
        }
        .tag(AppTab.directory)
    }
    .onChange(of: selection) { oldValue, newValue in
      print("\(oldValue) -> \(newValue)")
      if newValue == .directory {
        selection = oldValue
      }
    }
  }
}

How can I properly prevent tab selection without breaking the UI?

Broken UI

Upvotes: 1

Views: 43

Answers (1)

Danylo Horielov
Danylo Horielov

Reputation: 69

Maybe the best way to deal with it is to block TabView with overlay or ZStack. It just need to calculate the correct height:

    TabView(selection: $selection) {
      ...
    }
    .overlay(alignment: .bottom) {
      if isDisabled {
        HStack {
          Spacer()
          
          // Somehow correct the height
          Rectangle()
            .fill(.clear)
            .frame(height: 20)
            .padding(.vertical)
          
          Spacer()
        }
        .contentShape(Rectangle())
        .onTapGesture {
          if isDisabled {
            // show message etc.
          }
        }
        .background(.red.opacity(0.1))
      }
    }

Upvotes: 0

Related Questions