如何在 SwiftUI 中的 SegmentedPickerStyle 中为选取器提供图像和文本?

问题描述 投票:0回答:4

我正在使用 SwiftUI 构建一个选择器。

现在我想为每个选择添加一个图标和文本。所以它应该看起来像这样:

这可能吗?如果是的话怎么办?

或者苹果人机界面指南根本不推荐?

我已经尝试使用 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()
    }
}

但是框架将其分为四部分。

swiftui picker
4个回答
7
投票

您可以制作自定义选择器

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

}


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


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


0
投票

对于 iOS 16+,您可以使用

ImageRenderer
:

enum Category: String, CaseIterable, Identifiable {
    case person
    case more
    var id: String { self.rawValue }
    
    var systemImageName: String {
        switch self {
        case .person: return "person"
        case .more: return "ellipsis.circle"
        }
    }
}

struct ContentView: View {
    @State private var category = Category.person
    var body: some View {
        Picker("Category", selection: $category) {
            ForEach(Category.allCases) { category in
                if let catUIImage = ImageRenderer(content: buildCategoryView(category: category)).uiImage {
                    Image(uiImage: catUIImage)
                       .tag(category)
                }
            }
        }.pickerStyle(.segmented)
        .padding()
    }
    
    private func buildCategoryView(category: Category) -> some View {
        HStack {
            Image(systemName: category.systemImageName)
            Text(category.rawValue)
        }
    }
}

result category picker

© www.soinside.com 2019 - 2024. All rights reserved.