简体   繁体   English

SwiftUI:当同一表单上有选取器时,如何导航到 NavigationLink?

[英]SwiftUI: How can I navigate to a NavigationLink when there is a Picker on the same Form?

I want to put a "Scan" button on the same Form row as my Picker.我想在与我的选取器相同的表单行上放置一个“扫描”按钮。 The scanner View has its own NavigationLink that should pop up when the user taps the Scan button.扫描仪视图有自己的 NavigationLink,当用户点击 Scan 按钮时应该会弹出它。

What actually happens:实际发生的情况:

Both the Picker's NavigationLink and the Scanner's NavigationLink destination views appear at the same time. Picker 的 NavigationLink 和 Scanner 的 NavigationLink 目标视图同时出现。 When you tap either the Picker or the Scan button, you will randomly see one of the two destinations (assuming one is above the other in z-order).当您点击 Picker 或 Scan 按钮时,您将随机看到两个目的地之一(假设一个在 z 顺序中位于另一个之上)。

Here is what it will look like:这是它的样子:

在此处输入图像描述

Here is the code to reproduce this issue...这是重现此问题的代码...

    struct Truck: Hashable, Identifiable {
        var id: String
        var color: String
        var assetId: String
    }
    
    
    struct ContentView: View
    {
        var trucks = [
            Truck(id: "a", color: "Red", assetId: "1"),
            Truck(id: "b", color: "Green", assetId: "2"),
            Truck(id: "c", color: "Blue", assetId: "3")
        ]
        @State private var myTruck: Truck?
        
        var body: some View {
            GeometryReader { geometry in
                NavigationView {
                    Form {
                        HStack {
                            Picker("Choose a truck", selection: $myTruck) {
                                ForEach(trucks) { truck in
                                    Text(truck.color).tag(Optional.some(truck.self))
                                }
                            }
                            .frame(maxWidth: (geometry.size.width * 0.5))
                            .clipped()
                            Scanner(callback: { barcode in
                                myTruck = trucks.filter { $0.assetId == barcode }.first
                            })
                        }
                    }
                }
            }
        }
    }
    
    
    struct Scanner: View {
        @State private var isShowingScanner: Bool = false
        var callback: (String)->()
        var body: some View {
            ZStack { // makes NavigationLink not visible.
                NavigationLink(destination: Button("Scan 3 example", action: {
                    callback("3")
                    self.isShowingScanner = false
                }), isActive: $isShowingScanner) {
                    EmptyView()
                }
                .frame(width: 0, height: 0)
                Button("Scan", action: {
                    isShowingScanner = true
                })
            }
        }
    }

What I want to happen is if you tap the picker the list of options appears.我想要发生的是,如果您点击选择器,则会出现选项列表。 If you tap the scan button the scanner window should appear.如果您点击扫描按钮,扫描仪 window 应该会出现。 I thought about using tags for the various destination views, but I don't know how to do that on the Picker's destination.我考虑过为各种目的地视图使用标签,但我不知道如何在 Picker 的目的地上执行此操作。 Also, the scanner is a separate component, so I thought it would be able to control its own NavigationLink.此外,扫描仪是一个单独的组件,所以我认为它可以控制自己的 NavigationLink。

XCode 12.3 Swift 5 iOS 14.3 XCode 12.3 Swift 5 iOS 14.3

The solution is actually pretty simple.解决方案实际上非常简单。 The picker needs to be put in a frame that is less than the width of the row, and then clipped.选择器需要放在小于行宽的框架中,然后进行裁剪。 Otherwise, it will be selectable in the entire row.否则,它将在整行中可选。 I usually use a GeometryReader to get the proportions correct for each device.我通常使用 GeometryReader 来获取每个设备的正确比例。 Your row would look like this assuming a GeometryReader declared with a proxy named geometry:假设使用名为几何的代理声明 GeometryReader,您的行将如下所示:

HStack {
    Picker("Choose a truck", selection: $myTruck) {
        ForEach(trucks) { truck in
             Text(truck.color).tag(Optional.some(truck.self))
        }
    }
    .frame(width: (geometry.size.width * 0.5)) // uses 50% of the row. You can
                                               // make it any size you want.
    .clipped()

    Scanner(callback: { barcode in
        myTruck = trucks.filter { $0.assetId == barcode }.first
    })
}

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

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