系统CheckBox(Toggle组件)开关无法改造,系统提供的Picker组件可以采用RadioGroupPickerStyle()来实现单选,但UI过于单一。需要单选按钮可以分组且选中和非选中icon可自定义方便扩展,直接上代码
RadioButton.swift
import Foundation
import SwiftUI
public struct RadioButton<Image, Label> : View where Image : View, Label : View {
let id: String
@Binding var selectedId: String
@ViewBuilder var image: (Bool) -> Image
@ViewBuilder var label: () -> Label
public init(id: String, selectedId: Binding<String>, @ViewBuilder image: @escaping (Bool) -> Image, @ViewBuilder label: @escaping () -> Label) {
self.id = id;
self._selectedId = selectedId
self.image = image
self.label = label
}
public var body: some View {
Button {
self.selectedId = id
} label: {
HStack {
image(id == self.selectedId)
label()
}
.contentShape(Rectangle())
}
.buttonStyle(.plain)
}
}
调用举例
@State private var radioSelected: String = "0" //获取选中值且起到分组作用
@State private var radioSelected2: String = "0"
var body: some View {
VStack(spacing: 10) {
//===================组一
RadioButton(id: "0", selectedId: $radioSelected) { check in
Image(systemName: check ? "record.circle.fill" : "circle")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 14, height: 14, alignment: .center)
.foregroundColor(check ? Color.accentColor : Color.gray)
} label: {
Text("选项A")
.font(.system(size: 14))
}
RadioButton(id: "1", selectedId: $radioSelected) { check in
Image(systemName: check ? "record.circle.fill" : "circle")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 14, height: 14, alignment: .center)
.foregroundColor(check ? Color.accentColor : Color.gray)
} label: {
Text("选项B")
.font(.system(size: 14))
}
//===================组二
RadioButton(id: "0", selectedId: $radioSelected2) { check in
Image(systemName: check ? "checkmark.circle.fill" : "circle")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 14, height: 14, alignment: .center)
.foregroundColor(check ? Color.accentColor : Color.gray)
} label: {
Text("选项A")
.font(.system(size: 14))
}
RadioButton(id: "1", selectedId: $radioSelected2) { check in
Image(systemName: check ? "checkmark.circle.fill" : "circle")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 14, height: 14, alignment: .center)
.foregroundColor(check ? Color.accentColor : Color.gray)
} label: {
Text("选项B")
.font(.system(size: 14))
}
}
}