SwiftUI - наложение TabView с настраиваемым представлением

в моем последнем вопросе я задал вопрос о способе достижения следующего TabView оверлей в SwiftUI:

TabView Overlay

Еще раз спасибо @davidev за быструю поддержку.

Я модифицировал решение с возможностью применения модификатора условного представления. В этом случае .overlay ().

extension View {
    @ViewBuilder
    func `if`<Content: View>(_ condition: Bool, content: (Self) -> Content) -> some View {
        if condition {
            content(self)
        }
        else {
            self
        }
    }
}

Приведенный выше фрагмент кода дал мне возможность реализовать условную панель инструментов, которая будет появляться при переключении наблюдаемого объекта (например, при переключении из режима чтения в режим редактирования):

.if(class.observedObject){ view in
    view.overlay(ToolbarFromDavidev())
}

Это тоже работает, но в нем действительно есть ошибки (например, вся навигация по представлению сбрасывается, и я ограничил возможности стилизации).

Это подводит меня к моему вопросу: есть ли у кого-нибудь из вас эталонная реализация, которую я могу использовать для своей ориентации? Я хотел бы решить эту проблему с помощью ZStack, который я могу расположить над своим TabView, который я использую для навигации. Как показано на GIF-изображении выше. Однако я не могу разместить ZStack над TabView. Уже пробовали такие вещи, как игнорирование безопасных зон и т. Д.

Огромное спасибо!


person SwiftUIRookie    schedule 26.11.2020    source источник
comment
Отличное название панели инструментов просмотра от Davidev;)   -  person davidev    schedule 26.11.2020


Ответы (1)


Вы можете наложить TabView так:

TabView {
    ...
}
.overlay(
    VStack {
        Spacer()
        
        //Your View
            .frame(height: /*Height of the TabBar*/)
    }
    .edgesIgnoringSafeArea(.all)
)        

Теперь дело доходит до определения высоты TabBar. Поскольку он динамический (разные размеры экрана iPhone), один из способов будет таким:

@State private var tabBarHeight: CGFloat = .zero
...

TabView {
    //NavigationView
        .background(
            TabBarAccessor { tabBar in tabBarHeight = tabBar.bounds.height }
        )
}
.overlay(
    VStack {
        Spacer()
        
        //Your View
            .frame(height: tabBarHeight)
    }
    .edgesIgnoringSafeArea(.all)
) 

Пример решения

Все вместе - ваш код из вашего первого ответа изменен.

Код:

struct ContentView: View {
    
    @State private var tabBarHeight: CGFloat = .zero
    @State var isSelecting : Bool = false
    
    var body: some View {
        TabView {
            NavigationView {
                Text("First Nav Page")
            }
            .tabItem {
                Image(systemName: "house")
                Text("Home")
                
            }.tag(0)
            .background(
                TabBarAccessor { tabBar in tabBarHeight = tabBar.bounds.height }
            )
            
            NavigationView {
                Text("Second Nav Page")
            }
            .tabItem {
                Image(systemName: "gear")
                Text("Settings")
            }.tag(1)
            
            Text("No Nav Page")
                .tabItem{
                    Image(systemName: "plus")
                    Text("Test")
                }.tag(2)
        }
        .overlay(
            VStack {
                Spacer()
                Rectangle()
                    .foregroundColor(.green)
                    .frame(height: tabBarHeight)
            }
            .edgesIgnoringSafeArea(.all)
        )
    }
}

struct TabBarAccessor: UIViewControllerRepresentable {
    var callback: (UITabBar) -> Void
    private let proxyController = ViewController()

    func makeUIViewController(context: UIViewControllerRepresentableContext<TabBarAccessor>) ->
                              UIViewController {
        proxyController.callback = callback
        return proxyController
    }

    func updateUIViewController(_ uiViewController: UIViewController, context: UIViewControllerRepresentableContext<TabBarAccessor>) {
    }

    typealias UIViewControllerType = UIViewController

    private class ViewController: UIViewController {
        var callback: (UITabBar) -> Void = { _ in }

        override func viewWillAppear(_ animated: Bool) {
            super.viewWillAppear(animated)
            if let tabBar = self.tabBarController {
                self.callback(tabBar.tabBar)
            }
        }
    }
}

Примечание

Код TabBarAccessor, который я нашел: Здесь

person Mofawaw    schedule 26.11.2020
comment
Я не думаю, что это возможно, так как все внутри блока TabView помещается под ним. Но зачем тебе это так нужно? - person Mofawaw; 27.11.2020
comment
Чтобы добиться этого эффекта, который я проиллюстрировал на гифке - person SwiftUIRookie; 27.11.2020
comment
Хм, я думаю, что что-то неправильно понимаю. Эти двое противоречат? - person Mofawaw; 27.11.2020