繁体   English   中英

视图在 iOS 14 上的 SwiftUI 中呈现两次

[英]View presented twice in SwiftUI on iOS 14

我需要基于模型动态呈现视图,因此需要在 SwiftUI 视图外部决定呈现哪个视图和创建视图。 这在 iOS 13 上运行良好,但在 iOS 14 上,第一个视图再次呈现而不是第二个视图,尽管第二个视图确实按预期初始化,第一个视图第二次推送到导航堆栈上。

这似乎是一个 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)
    }
}

有四个几乎相同的视图:


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
        }
    }
}

您需要推迟构建导航链接目标视图(否则它会在第一次刷新时创建,您会看到效果)。

使用 Xcode 12 / iOS 14 测试

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

DeferView取自其他我的解决方案

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM