【问题标题】:Adding Segmented Style Picker to SwiftUI's NavigationView将分段样式选择器添加到 SwiftUI 的 NavigationView
【发布时间】:2020-02-02 23:45:51
【问题描述】:

问题和标题一样简单。我正在尝试将具有SegmentedPickerStyle 样式的Picker 放入SwiftUI 中的NavigationBar。它就像原生的Phone 应用程序的历史页面。图片如下

我曾在 Google 和 Github 上寻找项目、库或任何教程,但没有运气。我认为如果 nativa 应用程序和 WhatsApp 例如有它,那么它应该是可能的。任何帮助,将不胜感激。

【问题讨论】:

    标签: swift swiftui navigationbar picker navigationview


    【解决方案1】:

    您可以将 Picker 直接放入 .navigationBarItems。

    我遇到的唯一麻烦是让 Picker 居中。 (只是为了表明选择器确实可以在导航栏中,我将一种带有框架和几何阅读器的 hacky 解决方案放在一起。您需要找到一个合适的居中解决方案。)

    struct ContentView: View {
        @State private var choices = ["All", "Missed"]
        @State private var choice = 0
    
        @State private var contacts = [("Anna Lisa Moreno", "9:40 AM"), ("Justin Shumaker", "9:35 AM")]
    
        var body: some View {
            GeometryReader { geometry in
                NavigationView {
                    List {
                        ForEach(self.contacts, id: \.self.0) { (contact, time) in
                            ContactView(name: contact, time: time)
                        }
                        .onDelete(perform: self.deleteItems)
                    }
                    .navigationBarTitle("Recents")
                    .navigationBarItems(
                        leading:
                        HStack {
                            Button("Clear") {
                                // do stuff
                            }
                            Picker(selection: self.$choice, label: Text("Pick One")) {
                                ForEach(0 ..< self.choices.count) {
                                    Text(self.choices[$0])
                                }
                            }
                            .frame(width: 130)
                            .pickerStyle(SegmentedPickerStyle())
                                .padding(.leading, (geometry.size.width / 2.0) - 130)
                        },
                    trailing: EditButton())
                }
            }
        }
    
        func deleteItems(at offsets: IndexSet) {
            contacts.remove(atOffsets: offsets)
        }
    
    }
    
    struct ContactView: View {
        var name: String
        var time: String
    
        var body: some View {
            HStack {
                VStack {
                    Image(systemName: "phone.fill.arrow.up.right")
                    .font(.headline)
                    .foregroundColor(.secondary)
                    Text("")
                }
                VStack(alignment: .leading) {
                    Text(self.name)
                        .font(.headline)
                    Text("iPhone")
                        .foregroundColor(.secondary)
                }
                Spacer()
                Text(self.time)
                    .foregroundColor(.secondary)
            }
        }
    }
    

    【讨论】:

    【解决方案2】:

    SwiftUI 2 + 工具栏:

    struct DemoView: View {
    
        @State private var mode: Int = 0
    
        var body: some View {
            Text("Hello, World!")
                .toolbar {
                    ToolbarItem(placement: .principal) {
                        Picker("Color", selection: $mode) {
                            Text("Light").tag(0)
                            Text("Dark").tag(1)
                        }
                        .pickerStyle(SegmentedPickerStyle())
                    }
                }
        }
    }
    

    【讨论】:

    • 完美!将 .principal 用于顶部中心,将 .bottomBar 用于更容易到达的位置。
    • 对于任何想要Picker 的人,请使用其Intrinsic content size / ideal size,只需对其应用.fixedSize() 修饰符即可。
    【解决方案3】:

    对于那些想要使其成为死点的人,只需将两个HStack 放在每一侧,并使它们的宽度固定且相等。

    将此方法添加到View 扩展。

    extension View {
        func navigationBarItems<L, C, T>(leading: L, center: C, trailing: T) -> some View where L: View, C: View, T: View {
            self.navigationBarItems(leading:
                HStack{
                    HStack {
                        leading
                    }
                    .frame(width: 60, alignment: .leading)
                    Spacer()
                    HStack {
                        center
                    }
                     .frame(width: 300, alignment: .center)
                    Spacer()
                    HStack {
                        //Text("asdasd")
                        trailing
                    }
                    //.background(Color.blue)
                    .frame(width: 100, alignment: .trailing)
                } 
                //.background(Color.yellow)
                .frame(width: UIScreen.main.bounds.size.width-32)
            )
        }
    }
    

    现在您有一个View modifier,它的用法与navigationBatItems(:_) 相同。您可以根据需要编辑代码。

    使用示例:

    .navigationBarItems(leading: EmptyView(), center:       
        Picker(selection: self.$choice, label: Text("Pick One")) {
            ForEach(0 ..< self.choices.count) {
                 Text(self.choices[$0])
            }
         }
        .pickerStyle(SegmentedPickerStyle())
    }, trailing: EmptyView())
    

    更新

    存在leading 的问题,并且尾随项目违反了UINavigationBarContentViewsafeArea。在搜索时,我在answer 中遇到了另一个解决方案。它是一个名为SwiftUIX 的小助手库。如果您不想像我一样安装整个库,我为navigationBarItems 创建了一个gist。只需将文件添加到您的项目中。

    但不要忘记这一点,它拉伸Picker 以覆盖所有可用空间并迫使StatusView 变窄。所以我不得不像这样设置框架;

    .navigationBarItems(center:
        Picker(...) {
            ...
        }
        .frame(width: 150)
    , trailing:
        StatusView()
        .frame(width: 70)
    )
    

    【讨论】:

    • 那行不通,请提供一个可行的解决方案示例来澄清。
    【解决方案4】:

    如果你需要segmentcontroll 居中,你需要使用GeometryReader,下面的代码将提供picker 作为标题,以及尾随(右)按钮。

    您在左右两侧设置了两个宽度相同的视图,中间的视图将占据其余部分。 5 是神奇的数字,取决于您需要分段的宽度。 您可以进行试验并找到最适合您的方案。

       GeometryReader {
        
        Text("TEST")
         .navigationBarItems(leading:
                                        HStack {
                                            Spacer().frame(width: geometry.size.width / 5)
                                            Spacer()
                                            picker
                                            Spacer()
                                            Button().frame(width: geometry.size.width / 5)
                                               }.frame(width: geometry.size.width)
        
        }
    

    但更好的解决方案是如果您保存选取器大小然后计算其他帧大小,那么选取器在 ipad 和 iphone 上将是相同的

     @State var segmentControllerWidth: CGFloat = 0
    
        var body: some View {
                HStack {
                    Spacer()
                        .frame(width: (geometry.size.width / 2) - (segmentControllerWidth / 2))
                        .background(Color.red)
                    segmentController
                        .fixedSize()
                        .background(PreferenceViewSetter())
                    profileButton
                        .frame(width: (geometry.size.width / 2) - (segmentControllerWidth / 2))
                }
                .onPreferenceChange(PreferenceViewKey.self) { preferences in
                    segmentControllerWidth = preferences.width
                }
            }
        
        
        struct PreferenceViewSetter: View {
            var body: some View {
                GeometryReader { geometry in
                    Rectangle()
                        .fill(Color.clear)
                        .preference(key: PreferenceViewKey.self,
                                    value: PreferenceViewData(width: geometry.size.width))
                }
            }
        }
        
        struct PreferenceViewData: Equatable {
            let width: CGFloat
        }
        
        struct PreferenceViewKey: PreferenceKey {
            typealias Value = PreferenceViewData
        
            static var defaultValue = PreferenceViewData(width: 0)
        
            static func reduce(value: inout PreferenceViewData, nextValue: () -> PreferenceViewData) {
                value = nextValue()
            }
        }
    

    【讨论】:

      【解决方案5】:

      简单回答如何使段控制器居中并隐藏其中一个按钮。

      @State var showLeadingButton = true
          var body: some View {
              HStack {
                  Button(action: {}, label: {"leading"})
                                .opacity(showLeadingButton ? true : false)
      
      
                  Spacer()
      
                  Picker(selection: $selectedStatus,
                         label: Text("SEGMENT") {
                      segmentValues
                      }
                   .id(UUID())
                   .pickerStyle(SegmentedPickerStyle())
                   .fixedSize()
      
                   Spacer()
                   Button(action: {}, label: {"trailing"})
              }
              .frame(width: UIScreen.main.bounds.width)
          }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-05-26
        • 1970-01-01
        • 2021-09-13
        • 2021-06-20
        • 1970-01-01
        • 2020-05-16
        • 1970-01-01
        • 2020-04-19
        相关资源
        最近更新 更多