SwiftUI: как перейти с экрана C обратно на экран A (пропуская экран B) с помощью жеста пролистывания назад?

Я создаю приложение SwiftUI, в котором у меня есть три экрана (A, B и C), на которые осуществляется последовательная навигация. Поток выглядит следующим образом:

Screen A -> Screen B -> Screen C

Я хочу добиться следующего поведения:

Когда пользователь проводит назад с экрана C, он должен быть перенаправлен непосредственно на экран A, пропуская экран B (родное пролистывание назад для iOS).

На экране B вместо использования NavigationPath.append(viewC) я попытался установить путь следующим образом:

navigationPath = [.viewA, viewC]

Это приводит к полному отсутствию анимации при переходе к экрану C.

я тоже пробую это

{
navigationPath.removeLast
navigationPath.append(viewC)
}

Это приводит к полному отсутствию анимации при переходе к экрану C.

При появлении экрана C я попытался сбросить путь следующим образом:

navigationPath = [.viewA, viewC]

Здесь я получаю анимацию при переходе на экран C, когда работает onAppear, я видел анимацию обратного перехода (от viewC к viewC).

Есть ли лучший способ добиться такого поведения, при котором пролистывание назад с экрана C напрямую возвращается на экран A без каких-либо непреднамеренных анимаций? Любой совет будет принят с благодарностью!

Пример кода:

import SwiftUI

enum Route {
    case viewA, viewB, viewC
}

struct ContentView: View {
    @State private var path: [Route] = []

    var body: some View {
        NavigationStack(path: $path) {
            Text("This is root view")
            Button {
                path.append(.viewA)
            } label: {
                Text("Go to screen A")
            }
                .navigationDestination(for: Route.self) { route in
                    switch route {
                    case .viewA:
                        AView(path: $path)
                    case .viewB:
                        BView(path: $path)
                    case .viewC:
                        CView(path: $path)
                    }
                }
            
        }
    }
}

struct AView: View {
    @Binding var path: [Route]

    var body: some View {
        VStack {
            Text("This is Screen A")
            Button("Go to Screen B") {
                path.append(.viewB)
            }
        }
    }
}

struct BView: View {
    @Binding var path: [Route]

    var body: some View {
        VStack {
            Text("This is Screen B")
            Button("Go to Screen C") {
                path = [.viewA, .viewC]
            }
        }
    }
}

struct CView: View {
    @Binding var path: [Route]

    var body: some View {
        VStack {
            Text("This is Screen C")
        }
    }
}

#Preview {
    ContentView()
}

Было бы полезно, если бы вы предоставили воспроизводимый пример того, что вы описываете.

Andrei G. 22.08.2024 22:16

Да, добавил только сейчас

akhsary 22.08.2024 23:33

Спасибо. Итак, какова именно ваша цель? Чтобы была анимация при переходе с экрана B на C? Сейчас, насколько я понимаю, анимации от B до C нет.

Andrei G. 23.08.2024 19:07
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
3
50
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Предполагая, что вам нужно перейти к экрану C с анимацией, но без двойной анимации при переходе на экран C, я считаю, что следующее должно помочь:

struct BView: View {
    @Binding var path: [Route]
    
    var body: some View {
        VStack {
            Text("This is Screen B")
            Button("Go to Screen C") {
                    path.append(.viewC)
            }
        }
        .onDisappear { // <-- Here, add .onDisappear modifier to screen B
            var transaction = Transaction()
            transaction.disablesAnimations = true
            withTransaction(transaction) { // <-- Here, wrap path mutation in withTransaction, where transaction has disablesAnimations set to true
                path = [.viewA, .viewC]
            }
        }
    }
}

Вы были на правильном пути и пытались изменить путь после исчезновения экрана B, но это привело к двойной анимации экрана C: первый раз, когда он появился естественным образом, и второй раз, когда путь был сброшен как часть .onDisappear. действие экрана Б.

Судя по всему, начиная с iOS 16.2, вы можете отключить анимацию при нажатии и выталкивании NavigationStack, как показано выше. Этот ответ я нашла здесь.

Хотя двойная навигация по-прежнему происходит, как описано выше, она не будет заметна для пользователя. Вы можете увидеть это в действии, если присвоите каждому экрану заголовок, а затем заметите, что заголовок кнопки «Назад» меняется на заголовок экрана A после перехода на экран C (и после исчезновения экрана B).

Так что это может быть не идеально, если вы используете собственные заголовки навигации для каждого экрана.

Альтернативой этому подходу является скрытие кнопки «Назад» на экране C с помощью .navigationBarBackButtonHidden(true) и установка пользовательской кнопки «Назад» на панели инструментов с той же логикой, что и у кнопки на экране C (что-то вроде path = removeLast(2)), но это приведет к потере жест смахивания назад. Жест якобы можно восстановить, но сам я это не пробовал.

Кстати, я также пытался добавить ту же логику в onAppear экрана C вместо onDisappear экрана B, но это, похоже, приводит к мерцанию, которого нет в коде, как показано выше. Просто к вашему сведению.

Andrei G. 23.08.2024 20:34

Другие вопросы по теме