Как перейти от подпредставления TabView обратно к TabView в SwiftUI? - PullRequest
2 голосов
/ 01 мая 2020

В SwiftUI TabView должен быть представлением root. Поэтому вы не можете использовать NavigationLink для перехода к TabView. Например, у меня в приложении четыре экрана.

Экран A представляет собой TabView, который содержит экран B и экран C. Экран B - это список, в котором есть NavigationLink для перехода к деталям элемента списка (Экран D). Экран C - это информационное представление (это не важно в вопросе). Экран D - это экран сведений о элементе списка, сначала необходимо перейти на экране б, чтобы добраться сюда. Экран D, однако, имеет кнопку, которая должна выполнять сетевое действие в ViewModel, а затем по завершении перенаправлять вас на ScreenA.

Как с помощью экрана D можно перейти на два уровня назад к экрану root (Экран A )

Ответы [ 3 ]

1 голос
/ 01 мая 2020

Я сделал такой трюк, сработал для меня.

В SceneDelegate.swift я изменил автоматически сгенерированный код.


let contentView = ContentView()

if let windowScene = scene as? UIWindowScene {
   let window = UIWindow(windowScene: windowScene)
   // Trick here.
   let nav = UINavigationController(
       rootViewController: UIHostingController(rootView: contentView)
    )
    // I embedded this host controller inside UINavigationController
    //  window.rootViewController = UIHostingController(rootView: contentView)
    window.rootViewController = nav
    self.window = window
    window.makeKeyAndVisible()
}

Примечание : я ожидал встраивание TabView внутрь NavigationView будет выполнять ту же работу, но не сработало, это была причина для выполнения этого трюка.

Я полагаю, contentView - это представление, которое вы хотите отобразить ( один включает TabView)

Затем в любом представлении, перемещаемом из этого представления, вы можете позвонить

extension View {
    func popToRoot() {
        guard let rootNav = UIApplication.shared.windows.first?.rootViewController as? UINavigationController else { return }
        rootNav.popToRootViewController(animated: true)
    }
}

// Assume you eventually came to this view.
struct DummyDetailView: View {

    var body: some View {

        Text("DetailView")
           .navigationBarItems(trailing:
               Button("Pop to root view") {
                   self.popToRoot()
               }
           )
    }
}

// EDIT: Requested sample with a viewModel
struct DummyDetailViewWithViewModel: View {

    var viewModel: SomeViewModel = SomeViewModel()

    var body: some View {        
        Button("Complete Order!!") {
            viewModel.completeOrder(success: { _ in
                print("Order Completed")
                self.popToRoot()
            })
        }
    }
}
0 голосов
/ 04 мая 2020

Я решил это с self.presentationMode.wrappedValue.dismiss(). Это метод, который вызывается для возврата к View of Navigation Root. Здесь TestView - это ScreenA, ItemList - это ScreenB, InfoView - это Screen C, а ItemDetails - ScreenD.

import SwiftUI

struct TestView: View {
    @State private var currentTab: Tab = .list
    var body: some View {
        TabView(selection: $currentTab){
            ItemList()
                .tabItem{
                    Text("List")
            }
            .tag(Tab.list)
            .navigationBarHidden(false)
            InfoView()
                .tabItem{
                    Text("Info")
            }
            .tag(Tab.info)
            .navigationBarHidden(true)
        }
    }
}

struct ItemList: View {
    var body: some View {
        VStack{
            NavigationView{
                List {
                    NavigationLink(destination: ItemDetails()){
                        Text("Item")
                    }
                    NavigationLink(destination: ItemDetails()){
                        Text("Item")
                    }
                    NavigationLink(destination: ItemDetails()){
                        Text("Item")
                    }
                    NavigationLink(destination: ItemDetails()){
                        Text("Item")
                    }
                }.navigationBarTitle("Item List")
            }
        }
    }
}

struct InfoView: View {
    var body: some View {
        Text("This is information view")
    }
}

struct ItemDetails: View {
    @Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>
    @State private var loading = false
    var body: some View {
        ZStack {
            Text("Connecting...")
                .font(.title)
                .offset(y: -150)
                .pulse(loading: self.loading)
            VStack {
                Text("This is Item Details")
                Button("Connect"){
                    self.loading = true
                    DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
                        self.loading = false
                        self.presentationMode.wrappedValue.dismiss()
                    }
                }.padding()
            }
        }
    }
}

enum Tab {
    case list, info
}

extension View {
    func pulse(loading: Bool) -> some View {
        self
            .opacity(loading ? 1 : 0)
            .animation(
                Animation.easeInOut(duration: 0.5)
                    .repeatForever(autoreverses: true)
        )

    }
}
0 голосов
/ 01 мая 2020

Эффективный способ «выскочить» в ваше представление root - использовать модификатор isDetailLink для NavigationLink, который используется для навигации.

По умолчанию isDetailLink равно true. Этот модификатор используется для различных контейнеров представления, как на iPad, где подробное представление появилось бы на правой стороне.

Установка isDetailLink на false означает, что представление будет помещено поверх стека NavigationView, а также может быть удалено.

Наряду с установкой isDetailLink в false на NavigationLink, передайте привязку isActive каждому дочернему представлению назначения. Если вы хотите перейти к представлению root, установите значение false, и оно отключится:

import SwiftUI

struct ScreenA: View {
    @State var isActive : Bool = false

    var body: some View {
        NavigationView {
            NavigationLink(
                destination: ScreenB(rootIsActive: self.$isActive),
                isActive: self.$isActive
            ) {
                Text("ScreenA")
            }
            .isDetailLink(false)
            .navigationBarTitle("Screen A")
        }
    }
}

struct ScreenB: View {
    @Binding var rootIsActive : Bool

    var body: some View {
        NavigationLink(destination: ScreenD(shouldPopToRootView: self.$rootIsActive)) {
            Text("Next screen")
        }
        .isDetailLink(false)
        .navigationBarTitle("Screen B")
    }
}

struct ScreenD: View {
    @Binding var shouldPopToRootView : Bool

    var body: some View {
        VStack {
            Text("Last Screen")
            Button (action: { self.shouldPopToRootView = false } ){
                Text("Pop to root")
            }
        }.navigationBarTitle("Screen D")
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...