0

I have custom tab bar with two screen and I created a modifier to display custom sheet from bottom.

I don't want to use .sheet cause I can't change cornerRadius and also I want my sheet to be self-sized (dynamique height).

MainView:

struct MainView: View {

    var body: some View {

      ZStack(alignment: .bottom) {
          TabView(selection: $currentTab) {
              Screen1()
                 .tag(Tab.screen1)
              Screen2()
                 .tag(Tab.screen2)
          }
          .tabViewStyle(DefaultTabViewStyle())
          HStack {
              TabItemView(currentTab: $currentTab, tab: .screen1)
              TabItemView(currentTab: $currentTab, tab: .screen2)
          }
      }
  }

struct TabItemView: View {
    @Binding var currentTab: Tab
    var tab: Tab
    
    var body: some View {
        Button {
            currentTab = tab
        } label: {
            VStack {
                Image(tab.image)
                    .resizable()
                    .frame(width: 30, height: 30)
                    .frame(maxWidth: .infinity)
                Text(tab.rawValue)
            }.padding(.vertical, 10)
                
        }
    }
}

Custom BottomSheet :

struct BottomSheet<Content: View>: View {
    @Binding var isPresented: Bool
    let onDismiss: (() -> Void)?
    let content: Content
    
    init(isPresented: Binding<Bool>, onDismiss: (() -> Void)? = nil, content: @escaping () -> Content) {
        self._isPresented = isPresented
        self.onDismiss = onDismiss
        self.content = content()
    }
    
    var isiPad: Bool {
        UIDevice.current.userInterfaceIdiom == .pad
    }
    
    public var body: some View {
        ZStack {
            if isPresented {
                Color.black.opacity(0.3)
                    .edgesIgnoringSafeArea(.all)
                    .transition(.opacity)
                    .zIndex(1)
                    .onTapGesture {
                        dismiss()
                    }
                container
                    .ignoresSafeArea(.container, edges: .bottom)
                    .transition(isiPad ? AnyTransition.opacity.combined(with: .offset(x: 0, y: 200)) : .move(edge: .bottom))
                    .zIndex(2)
            }
        }.animation(.spring(response: 0.35, dampingFraction: 1), value: isPresented)
    }
    
    private var container: some View {
        VStack {
            Spacer()
            
            if isiPad {
                card.aspectRatio(1.0, contentMode: .fit)
                Spacer()
            } else {
                card
            }
        }
    }
    
    private var card: some View {
        VStack(alignment: .trailing, spacing: 0) {
            content
        }
        .background(Color.background)
        .clipShape(RoundedRectangle(cornerRadius: .radius_xl))
    }
    
    func dismiss() {
        withAnimation {
            isPresented = false
        }
        
        onDismiss?()
    }
}

extension View {
    func bottomSheet<Content: View>(isPresented: Binding<Bool>, onDismiss: (() -> Void)? = nil, backgroundColor: Color = Color(.systemGray6), @ViewBuilder content: @escaping () -> Content) -> some View {
       ZStack {
           self
           BottomSheet(isPresented: isPresented, onDismiss: onDismiss) { content() }
            }
        }
}

Screen 1:

struct Screen1: View {

    ...

    @State var isPresented = false

    ...

    var body: some View {

        Button(action: {
            isPresented = true
        }, label: {
            HStack {
                Spacer()
                Image(systemName: "plus")
                Text("Add")
                Spacer()
            }
        })
        .bottomSheet(isPresented: $isPresented) {
           PopupView(isPresented: $isPresented, message: "") {
              //Action when user confirm
           }
        }
    }

}

The problem is bottom sheet is presented behind my tab bar, when I move my .bottomSheet modifier to MainView it's working perfectly but in my case I want to use .bottomSheet in child views cause I have actions to do when user tap confirm or cancel button.

PS: I want to mimic .sheet behavior, always presenting view on top of root

OuSS
  • 19
  • 3

0 Answers0