SwiftUI:如何在SwiftUI中实现单选按钮

1szpjjfi  于 2023-03-28  发布在  Swift
关注(0)|答案(4)|浏览(322)

我正在创建一个简单的表单应用程序。在其中,我有复选框和单选按钮,但我不知道如何操作。
我已经做了下面的代码来动态改变所选选项的颜色。但它可以选择多个值。我需要选择5个值中的一个值,就像一个单选按钮。
我正在按第二个单选按钮。现在如果我选择第四个单选按钮,第二个应该取消选择,第四个应该被选中。

struct DCTableCell: View {

    @Binding var dcValue: String

    @State var isSelected: Bool = false

    var body: some View {

        Button(action: {
            print("Tapped")
            self.isSelected.toggle()
        }){
            ZStack {
                RoundedRectangle(cornerRadius: 8)
                    .stroke(self.isSelected ? Color.init("borderSelected"): Color.init("border"))
                    .frame(height: 56)
                    .foregroundColor(.clear)

                HStack {
                    Text(dcValue)
                        .font(.custom("Montserrat", size: 16))
                        .fontWeight(.medium)
                        .foregroundColor(self.isSelected ? Color.init("borderSelected") : .white)
                        .padding()

                    Spacer()

                    ZStack {
                        Circle()
                            .stroke(self.isSelected ? Color.init("borderSelected") : Color("circleBorder"))
                            .frame(width: 18, height: 18)
                            .padding()

                        Circle()
                            .frame(width: 10, height: 10)
                            .foregroundColor(self.isSelected ? Color.init("borderSelected"): Color.clear)

                    }
                }
            }
        }
    }
}
2w3rbyxf

2w3rbyxf1#

看看这个...一个易于使用的SwiftUI RadiobuttonGroup for iOS
你可以这样使用它:

RadioButtonGroup(items: ["Rome", "London", "Paris", "Berlin", "New York"], selectedId: "London") { selected in
                print("Selected is: \(selected)")
            }

代码如下:

struct ColorInvert: ViewModifier {

    @Environment(\.colorScheme) var colorScheme

    func body(content: Content) -> some View {
        Group {
            if colorScheme == .dark {
                content.colorInvert()
            } else {
                content
            }
        }
    }
}

struct RadioButton: View {

    @Environment(\.colorScheme) var colorScheme

    let id: String
    let callback: (String)->()
    let selectedID : String
    let size: CGFloat
    let color: Color
    let textSize: CGFloat

    init(
        _ id: String,
        callback: @escaping (String)->(),
        selectedID: String,
        size: CGFloat = 20,
        color: Color = Color.primary,
        textSize: CGFloat = 14
        ) {
        self.id = id
        self.size = size
        self.color = color
        self.textSize = textSize
        self.selectedID = selectedID
        self.callback = callback
    }

    var body: some View {
        Button(action:{
            self.callback(self.id)
        }) {
            HStack(alignment: .center, spacing: 10) {
                Image(systemName: self.selectedID == self.id ? "largecircle.fill.circle" : "circle")
                    .renderingMode(.original)
                    .resizable()
                    .aspectRatio(contentMode: .fit)
                    .frame(width: self.size, height: self.size)
                    .modifier(ColorInvert())
                Text(id)
                    .font(Font.system(size: textSize))
                Spacer()
            }.foregroundColor(self.color)
        }
        .foregroundColor(self.color)
    }
}

struct RadioButtonGroup: View {

    let items : [String]

    @State var selectedId: String = ""

    let callback: (String) -> ()

    var body: some View {
        VStack {
            ForEach(0..<items.count) { index in
                RadioButton(self.items[index], callback: self.radioGroupCallback, selectedID: self.selectedId)
            }
        }
    }

    func radioGroupCallback(id: String) {
        selectedId = id
        callback(id)
    }
}

struct ContentView: View {
    var body: some View {
        HStack {
            Text("Example")
                .font(Font.headline)
                .padding()
            RadioButtonGroup(items: ["Rome", "London", "Paris", "Berlin", "New York"], selectedId: "London") { selected in
                print("Selected is: \(selected)")
            }
        }.padding()
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

struct ContentViewDark_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
        .environment(\.colorScheme, .dark)
        .darkModeFix()
    }
}

oiopk7p5

oiopk7p52#

好吧,这不是一个理想的解决方案,但它的工作,并希望打开你的眼睛,以改善你所拥有的。我给予每个RadioButton一个ID,当选定的ID更改它更新:

struct DCTableCell: View {

    var id: Int
    @Binding var dcValue: String
    @Binding var selectedID: Int

    var body: some View {
        Button(action: {
            print("Tapped")
            self.selectedID = self.id
        }){
            ZStack {
                RoundedRectangle(cornerRadius: 8)
                    .stroke(self.id == self.selectedID ? Color.blue : Color.white)
                    .frame(height: 56)
                    .foregroundColor(.clear)

                HStack {
                    Text(dcValue)
                        .font(.custom("Montserrat", size: 16))
                        .fontWeight(.medium)
                        .foregroundColor(self.id == self.selectedID ? .blue : .white)
                        .padding()

                    Spacer()

                    ZStack {
                        Circle()
                            .stroke(self.id == self.selectedID ? Color.blue : .black)
                            .frame(width: 18, height: 18)
                            .padding()

                        Circle()
                            .frame(width: 10, height: 10)
                            .foregroundColor(self.id == self.selectedID ? Color.blue: Color.clear)

                    }
                }
            }
        }
    }
}

这里是如何使用它。也许你应该创建一个数组,其中包含id和你想要传入的字符串。

struct ContentView: View {
    @State var str = "lolz"
    @State var selectedID = -1

    var body: some View {
        VStack {
            ForEach((1...5), id: \.self) { index in
                DCTableCell(id: index, dcValue: self.$str, selectedID: self.$selectedID)
            }
        }
    }
}

希望这能有所帮助!

q5lcpyga

q5lcpyga3#

我有一个类似的解决方案,使用标签作为hashable标签,这使得它非常直接。所以你只需要设置外层:CustomDCPicker,像一个普通的picker。

UIHostingController(rootView: CustomDCPicker())

        struct CustomDCPicker: View {
            @State var dcValue: String = ""
            var body: some View {
                VStack{
                    Text(dcValue).bold()
                    DCTable.init(dcValue: $dcValue, Labels: ["sample1","sample2","sample3","sample4","sample5"])
                }
            }
        }

        struct DCTable: View {
            @Binding var dcValue: String
            var Labels: [String] = []
        var body: some View {
            ForEach(Labels, id:\.self){
                DCTableCell(dcValue: self.$dcValue, myLabel: $0)
            }
        }
        }

        struct DCTableCell: View {
            @Binding var dcValue: String
            var isSelected: Bool {
                get{   self.dcValue == self.myLabel}
            }
            var myLabel : String
            var body: some View {
                Button(action: {
                    print("Tapped")
                    self.dcValue = self.myLabel
                }){
                    ZStack {
                        RoundedRectangle(cornerRadius: 8.0)
                            .stroke(self.isSelected ? Color.red: Color.yellow)
                            .frame(height: 56)
                            .foregroundColor(.clear)

                        HStack {
                            Text(myLabel)
                                //.font(.custom("Montserrat", size: 16))
                                .fontWeight(.medium)
                                .foregroundColor(self.isSelected ? Color.red : .black)
                                .padding()

                            Spacer()

                            ZStack {
                                Circle()
                                    .stroke(self.isSelected ? Color.red : Color.black)
                                    .frame(width: 18, height: 18)
                                    .padding()

                                Circle()
                                    .frame(width: 10, height: 10)
                                    .foregroundColor(self.isSelected ? Color.red: Color.clear)

                            }
                        }
                    }
                }
            }
        }
7lrncoxx

7lrncoxx4#

这里是另一个产品,它可能比已经提供的其他解决方案更简单。

  • 它可以与Binding结合使用,以获得任何Equatable值。
  • 如果Binding的值对应于提供给init的onState,则按钮的外观为on...否则不是。
  • 使用此处提供的样式,按钮的外观与iOS设备设置中用于亮/暗模式的按钮相同。
  • 当用户选择使用更大的字体时,按钮会变大。这是通过@ScaledMetric属性 Package 器实现的。
import SwiftUI

/// A toggle that is used in the context of radio controls
struct RadioButton<T: Equatable>: View {

    /// The size for the button
    @ScaledMetric(relativeTo: .body) private var size: CGFloat = 20

    /// The line width for stroking the unselected button
    @ScaledMetric(relativeTo: .body) private var lineWidth: CGFloat = 1.5

    /// The setting state that applies when the button is on
    private let onState: T

    /// Binding to the state variable that controls the button look
    @Binding private var selectedState: T

    /// Creates a radio button to reflect the supplied binding.
    /// No gesture handlers are added here
    init(onState: T, selectedState: Binding<T>) {
        self.onState = onState
        self._selectedState = selectedState
    }

    var body: some View {
        if selectedState == onState {

            // Show a round shape with a transparent tick
            Image(systemName: "checkmark.circle.fill")
                .resizable()
                .scaledToFill()
                .foregroundColor(.blue)
                .frame(width: size + lineWidth, height: size + lineWidth)
        } else {

            // Just show an empty ring
            Circle()
                .stroke(lineWidth: lineWidth)
                .foregroundColor(Color(UIColor.tertiaryLabel))
                .frame(width: size, height: size)
                .padding(lineWidth / 2)
        }
    }
}

下面是如何使用它:

struct ContentView: View {

    enum City: String, CaseIterable {
        case Rome = "Rome"
        case London = "London"
        case Paris = "Paris"
        case Berlin = "Berlin"
        case NewYork = "New York"
    }

    @State private var city = City.London

    private func radioButtonItem(choice: City) -> some View {
        HStack {
            RadioButton(onState: choice, selectedState: $city)
            Text(choice.rawValue)
        }
        .onTapGesture { city = choice }
    }

    var body: some View {
        VStack(alignment: .leading) {
            ForEach(City.allCases, id: \.self) { city in
                radioButtonItem(choice: city)
            }
        }
    }
}

相关问题