Отсутствие смены фаз сцены - PullRequest
5 голосов
/ 10 июля 2020

Я пытаюсь выполнить код, который я ранее поместил в делегат моего приложения, например, сохранение контекста управляемого объекта при входе в фоновый режим. Я поместил вызов в .onChange для scenePhase, но ничего не получаю. Вот пример проекта:

import SwiftUI

@main
struct PhaseApp: App {
    @Environment(\.scenePhase) private var scenePhase
    
    var body: some Scene {
        WindowGroup {
            Text("Hello, world.")
        }
        .onChange(of: scenePhase) { phase in
            switch phase {
            case .active:
                print("Active")
            case .background:
                print("Background")
            case .inactive:
                print("Inactive")
            @unknown default: break
            }
        }
    }
}

Я ожидал бы получить команду печати в симуляторе или на моем тестовом устройстве всякий раз, когда я нажимаю «Домой» или касаюсь приложения, но ничего не происходит.

Ответы [ 3 ]

2 голосов
/ 23 июля 2020

Я тестировал Xcode 12 beta 3 и iOS / iPadOS 14 beta 3 и вот что я нашел. Обратите внимание, что во многом это связано с поддержкой нескольких windows, но в «жизненном цикле SwiftUI» это по умолчанию включается, поэтому я подозреваю, что он у вас уже активен. В моем первоначальном случае я переносил существующее приложение SwiftUI с SceneDelegate на использование новой структуры App, поэтому у меня уже была активна поддержка нескольких окон.

Вот тест View Я использую в новом приложении для тестирования:

struct ContentView: View {
    @Environment(\.scenePhase) private var scenePhase

    var body: some View {
        Text("Hello, world!").padding()
            .onChange(of: scenePhase) { phase in
                switch phase {
                case .background:
                    print("PHASECHANGE: View entered background")
                case .active:
                    print("PHASECHANGE: View entered active")
                case .inactive:
                    print("PHASECHANGE: View entered inactive")
                @unknown default:
                    print("PHASECHANGE: View entered unknown phase.")
                }
            }
    }
}

(у меня идентичный код в App и Scene, но они никогда ничего не печатают.)

  1. The * Документация 1017 * ScenePhase утверждает, что вы можете объявить onChange внутри App, Scene или a View. Я не вижу, чтобы версии уровня App или Scene когда-либо выполнялись, ни при каких обстоятельствах, которые я могу разработать, а версии уровня View, похоже, не выполняются полностью правильно.

  2. На оборудовании, которое не поддерживает несколько windows (я использую iPod touch 7-го поколения), закрытие уровня View выполняется каждый раз. (Полное раскрытие, этот iPod Touch все еще работает с бета-версией 2, но я не думаю, что это будет иметь значение. Когда я обновлю его до b3, я упомяну об этом здесь, если это имеет значение.) EDIT (Это имело значение.) На оборудовании с бета-версией 2, которое не поддерживает несколько windows (iPod Touch 7-го поколения), я вижу приложение go в фоновом режиме, обратно на передний план и так далее. При каждом запуске приложения я буду видеть печать «Просмотр введен активен».

  3. На оборудовании, которое поддерживает несколько windows (я использую более старый iPad Pro с разъем Lightning) Я не вижу, чтобы происходило создание начальной сцены. (При первом запуске не появляется сообщение «Просмотр введен активным».) Я вижу последующие переходы фон / передний план. Если я создам новую сцену из многозадачного пользовательского интерфейса iPad, сцена секунда вызовет журнал «Просмотр введенных активных». К сожалению, я не проводил этот тест на iPad с бета-версией 2, поэтому я не могу сказать, изменилось ли поведение с b3 или нет.

  4. На моем iPod Touch работает iOS 14 beta 3 Я наблюдаю то же поведение, что и iPad: при первом запуске не выводятся сообщения об изменении фазы из представления, но отображаются последующие изменения фона / переднего плана.

  5. simulator он всегда ведет себя как аппаратное обеспечение iPad, даже когда я моделирую iPod Touch. Я подозреваю, что это потому, что симулятор работает под капотом Ma c и таким образом получает "поддержку" нескольких окон. Но я вижу сообщения, когда помещаю приложение в фоновый режим во время работы в симуляторе, мне просто не хватает начального сообщения «Просмотр введен активным», которое я получаю от реального оборудования.

Последнее замечание: когда я возвращаю приложение с переднего плана, я сначала вижу «Просмотр введен неактивным», а затем я вижу «Просмотр введен активным». Когда я использую приложение в фоновом режиме, я вижу «Просмотр введенного неактивного», а затем «Просмотр введенного фона». Я думаю, что это ожидаемое поведение, но, поскольку другие части кажутся сломанными, я хотел упомянуть об этом.

TL; DR:

Я думаю, вы сможете увидеть большинство ScenePhase изменений с View, но вы пропустите первоначальный запуск приложения на iPad или в симуляторе . И, надеюсь, они появятся, как ожидалось, для объектов App и Scene в более поздней бета-версии?

2 голосов
/ 10 июля 2020

Использовать внутри сцены root вид (обычно ContentView)

Протестировано с Xcode 12 / iOS 14 как работало.

struct ContentView: View {
    @Environment(\.scenePhase) private var scenePhase
    var body: some View {
        TestView()
            .onChange(of: scenePhase) { phase in
                switch phase {
                    case .active:
                        print(">> your code is here on scene become active")
                    case .inactive:
                        print(">> your code is here on scene become inactive")
                    case .background:
                        print(">> your code is here on scene go background")
                    default:
                        print(">> do something else in future")
                }
            }
    }
}
0 голосов
/ 11 июля 2020

Вы можете использовать следующее расширение:

public extension View {
    func OnScenePhaseChange(phase: ScenePhase, action: @escaping () -> ()) -> some View
    {
        self.modifier( OnScenePhaseChangeModifier(phase: phase, action: action) )
    }
}

public struct OnScenePhaseChangeModifier: ViewModifier {
    @Environment(\.scenePhase) private var scenePhase
    
    public let phase: ScenePhase
    
    public let action: () -> ()
    
    public func body(content: Content) -> some View {
        content
            .onChange(of: scenePhase) { phase in
                if (phase == phase) {
                    action()
                }
            }
    }
}

Окончательное использование:

ContentView()
     .OnScenePhaseChange(phase: .active)     { print("scene activated!") }
     .OnScenePhaseChange(phase: .background) { print("scene backgrounded!") }
     .OnScenePhaseChange(phase: .inactive)   { print("scene inactive!") }
...