ios - 在 iOS 14 上的 SwiftUI 中显示两次 View

标签 ios swift view navigation swiftui

我需要基于模型动态呈现 View ,因此要呈现哪个 View 和创建 View 的决定需要在 SwiftUI View 之外。这在 iOS 13 上运行良好,但在 iOS 14 上,第一个 View 再次出现而不是第二个 View ,尽管第二个 View 确实按预期进行了初始化,第一个 View 被第二次推送到导航堆栈上。
这似乎是一个 iOS 14 错误,但也许我做错了什么,所以在向 Apple 提交错误报告之前在这里询问。
这是我正在尝试做的简化版本,此代码适用于 iOS 13,但不适用于 iOS 14。


/// Defines the interface for a Screen that is presented dynamically using the Coordinator & ScreenProvider
protocol Screen {
    
    var coordinator: Coordinator { get }
    var pushNext: Bool { get set }
    var sheetNext: Bool { get set }
    
    func screenIsComplete() throws
}


/// Creates Screens on request
struct ScreenProvider {

    /// Creates a Screen when receiving a `screenId` & wraps it in an AnyView
    func screen(for screenId: String, coordinator: Coordinator) -> AnyView {
        
        print("Getting screen(for screenId: \(screenId))")
        
        switch screenId {
        case "View1":
            return AnyView(View1(coordinator: coordinator))
        case "View2":
            return AnyView(View2(coordinator: coordinator))
        case "View3":
            return AnyView(View3(coordinator: coordinator))
        case "View4":
            return AnyView(View4(coordinator: coordinator))
        default:
            fatalError()
        }
    }
}

/// What should the current Screen do after it has completed
enum ScreenPresentationAction {
    case pushNext
    case sheetNext
    case doNothing
}

/// Determines what action should be taken after a screen has completed and serves the next Screen to the current Screen 
final class Coordinator {
    
    let screenProvider: ScreenProvider
    private var screenIndex: Int = 1
    
    init(screenProvider: ScreenProvider) {
        self.screenProvider = screenProvider
    }
    
    /// What should the current Screen do after it has completed
    func nextActionAfterScreenCompletion() throws -> ScreenPresentationAction {
        if screenIndex < 4 {
            screenIndex += 1
        }
        if screenIndex == 4 {
            return .sheetNext
        }
        return .pushNext
    }
    
    /// Get the next screen to be presented
    func nextScreen() -> some View {
    
        let screenId = "View\(screenIndex)"
        print("Getting nextScreen() for index \(screenIndex)")
        return screenProvider.screen(for: screenId, coordinator: self)
    }
}
有四个几乎相同的 View :

struct View1: View, Screen {
    
    //------------------------------------
    // MARK: Screen
    //------------------------------------
    var coordinator: Coordinator
    @State var pushNext: Bool = false
    @State var sheetNext: Bool = false

    //------------------------------------
    // MARK: Properties
    //------------------------------------
    
    // # Body
    var body: some View {
        
        VStack {
            
            Spacer()
            Text("View 1")
            Spacer()
            Button(action: {
                 try! self.screenIsComplete()
            }) {
                Text("Next")
            }
            Spacer()
            NavigationLink(destination: self.coordinator.nextScreen(), isActive: self.$pushNext) {
                EmptyView()
            }
        }
        .sheet(isPresented: self.$sheetNext) {
            self.coordinator.nextScreen()
        }
    }
}

extension View1 {
    
    func screenIsComplete() throws {
        
        let action = try coordinator.nextActionAfterScreenCompletion()
        switch action {
        case .pushNext:
            self.pushNext = true
        case .sheetNext:
            self.sheetNext = true
        case .doNothing:
            break
        }
    }
}

最佳答案

您需要推迟构建导航链接目标 View (否则它会在第一次刷新时立即创建并且您会看到效果)。
使用 Xcode 12/iOS 14 测试

NavigationLink(destination: DeferView { self.coordinator.nextScreen() }, // << here !!
  isActive: self.$pushNext) {
    EmptyView()
}
DeferView取自 other my solution

关于ios - 在 iOS 14 上的 SwiftUI 中显示两次 View ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64062153/

相关文章:

ios - 导航到特定的 TabBar 项

java - 如何从 JFileChooser(JAVA Swing) 禁用文件操作、文件选择和过滤面板?

Android Spinner 的 getSelectedView() 返回 null

ios - 当用户更改联系人访问权限时,iOS 6 中的应用程序崩溃

ios - 调整导航后退按钮 iOS 13

ios - VPImageCropperViewController API 问题

ios - 使用 AVAudioRecorder 录制音频时的实时音高变化

ios - iOS(快速)在初始化后更改CAEmitterCell的属性

ios - 定义 Bundle.main.url 以在 iOS9 及以上版本的 Swift 4 中使用 Core Data

php - Laravel 5.3 View 之间传递数据