我正在使用 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()
}
}
但是框架将其分为四部分。
您可以制作自定义选择器
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)
}
这是一种使用 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)
}
}
这是使用 @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()
}
}
对于 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)
}
}
}