如何在SwiftUI的SegmentedPickerStyle中为Picker添加图像和文本?

5
我正在使用SwiftUI构建一个选择器。
现在我想为每个选项添加一个图标和文本。它应该看起来像这样:

enter image description here

这是否可能?如果是,该如何做?

或者苹果的人机界面指南完全不推荐吗?

我已尝试使用HStack将图像和文本包装在一起。

enum Category: String, CaseIterable, Identifiable {
    case person
    case more

    var id: String { self.rawValue }
}

struct ContentView: View {

    @State private var category = Category.person

    var body: some View {
        Picker("Category", selection: $category) {
            HStack {
                Image(systemName: "person")
                Text("Person")
            }.tag(Category.person)
            HStack {
                Image(systemName: "ellipsis.circle")
                Text("More")
            }.tag(Category.more)
        }.pickerStyle(SegmentedPickerStyle())
        .padding()
    }
}

但是框架将其分成了四个部分。

enter image description here


没有一个最小可复现示例,我们无法根据你尝试过的内容来帮助你进行故障排除。 - lorem ipsum
似乎没有一种简单的方法来做到这一点。在这里,您真的不应该混合图像和文本,应该选择其中一种而不是两者兼有。 - George
这原本看起来是一个简单的问题:只需用Label代替HStack...但它不起作用?!我还尝试使用自定义的LabelStyle...但是没有成功。似乎SegmentedPickerStyle只使用了两个提供的视图中的一个。选择WheelPickerStyle会按预期显示标签。这绝对是分段选择器的问题。 - pd95
1
根据苹果分段控件人机界面指南,同时使用文本和图像绝对不是期望的行为。因此,这不是SwiftUI的错误,而是一种特性:您无法做错它 :-) - pd95
3个回答

5

您可以制作自定义的选择器

struct ContentView: View {
var body: some View {
    Home()
}
}

struct Home: View {

@State var index = 0
var body: some View {
    
    VStack {
        HStack {
            Text("Picker with icon")
                .font(.title)
                .fontWeight(.bold)
                .foregroundColor(.black)
            Spacer(minLength: 0)
        }
        .padding(.horizontal)
        
        
        HStack(spacing: 0){
            HStack{
                Image(systemName: "person")
                    .foregroundColor(self.index == 0 ? .black : .gray)
                Text("Person")
                    .foregroundColor(self.index == 0 ? .black : .gray)
                
                
            }
            .padding(.vertical, 10)
            .padding(.horizontal, 35)
            .background((Color.white).opacity(self.index == 0 ? 1 : 0))
            .clipShape(Capsule())
            .onTapGesture {
                self.index = 0
            }
            
            HStack{
                Image(systemName: "ellipsis.circle")
                    .foregroundColor(self.index == 1 ? .black : .gray)
                Text("More")
                    .foregroundColor(self.index == 1 ? .black : .gray)
                
                
            }
            .padding(.vertical, 10)
            .padding(.horizontal, 35)
            .background((Color.white).opacity(self.index == 1 ? 1 : 0))
            .clipShape(Capsule())
            .onTapGesture {
                self.index = 1
            }
        }
        .padding(3)
        .background(Color.black.opacity(0.06))
        .clipShape(Capsule())
        Spacer(minLength: 0)
    }
    .padding(.top)
}

} example


非常感谢您!我花了很多时间找到如何制作类似的分段控制器,特别是如何改变分段控制器的高度。 - Abrcd18
1
请问在选择特定段时,您如何显示视图? - Abrcd18
1
嵌入Zstack并添加条件(如果index == 0 {View1} else if index == 1 {View2}....) - AdR
@AdR,你能否请在回答中增加更多系统动画效果?同时,也请提供一个选择示例以改善整体效果。谢谢! - KAMIKAZE

1
这是一种使用Apple Picker并获得所需输出的方法:
enum Category: String, CaseIterable, Identifiable {
    case person
    case more
    
    var id: String { self.rawValue }
}


struct ContentView: View {
    
    @State private var category = Category.person
    
    private var view1: some View { HStack { Image(systemName: "person"); Text("Person") } }
    private var view2: some View { HStack { Image(systemName: "ellipsis.circle"); Text("More") } }
    
    @State private var uiImage1: UIImage? = nil
    @State private var uiImage2: UIImage? = nil
    
    var body: some View {
        
        return Picker("Category", selection: $category) {
            
            if let unwrappedUIImage1 = uiImage1 {
                Image(uiImage: unwrappedUIImage1)
                    .tag(Category.person)
                
            }
            
            if let unwrappedUIImage2 = uiImage2 {
                Image(uiImage: unwrappedUIImage2)
                    .tag(Category.more)
                
            }
            
        }
        .pickerStyle(SegmentedPickerStyle())
        .padding()
        .onAppear() {
            
            DispatchQueue.main.async {
                uiImage1 = viewToUIImageConverter(content: view1)
                uiImage2 = viewToUIImageConverter(content: view2)
            }
            
            print("Your selection is:", category.rawValue)
        }
        .onChange(of: category, perform: { newValue in print("Your selection is:", newValue.rawValue) })
        
    }
}



func viewToUIImageConverter<Content: View>(content: Content) -> UIImage? {
    
    let controller = UIHostingController(rootView: content)
    let view = controller.view
    
    let targetSize = controller.view.intrinsicContentSize
    
    view?.bounds = CGRect(origin: .zero, size: targetSize)
    view?.backgroundColor = UIColor.clear
    
    let renderer = UIGraphicsImageRenderer(size: targetSize)
    
    return renderer.image { _ in
        view?.drawHierarchy(in: controller.view.bounds, afterScreenUpdates: true)
    }
    
}

enter image description here


1
我尝试了上面的代码,所有选项都是白色的,没有图标或文本显示。有什么线索吗? - Joe Huang
只有白色适用于iOS 16。 - KAMIKAZE

1

这里有另一种使用 @ViewBuilder 构建项目的可能性。它可以根据您的需求进行调整,希望能够帮到您!

import SwiftUI

struct CustomPicker<T: Identifiable & Equatable, C: RandomAccessCollection<T>, Content: View>: View  {
    let items: C
    @Binding var selection: T
    @ViewBuilder let itemBuilder: (T) -> Content
    var body: some View {
        HStack(spacing: 0) {
            ForEach(items) { source in
                Button {
                    selection = source
                } label: {
                    itemBuilder(source)
                        .padding()
                        .background(selection == source ? Color.white : Color.clear)
                        .cornerRadius(7)
                        .foregroundColor(Color.black)
                }
                .buttonStyle(PlainButtonStyle())
                .animation(.default, value: selection)
            }
        }
        .padding(4)
        .background(Color.gray.opacity(0.4))
        .cornerRadius(7)
        
    }
}

struct Item: Identifiable, Equatable {
    let id: String
    let name: String
}

struct CustomPicker_Previews: PreviewProvider {
    struct Container: View {
        @State var selection = Item(id: "item-1", name: "Item 1")
        var body: some View {
            CustomPicker(items: [Item(id: "item-1", name: "Item 1"), Item(id: "item-2", name: "Item 2")], selection: $selection) { item in
                HStack {
                    Image(systemName: "square.and.arrow.up")
                    Text("\(item.name)")
                }
                .padding()
            }
        }
    }
    
    static var previews: some View {
        Container()
    }
}

Preview


可以更像系统动画吗?! - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接