完成多项选择后如何启用按钮以及如何禁用其他选择?

问题描述

我认为有10种不同的选择。我要在做出10个选择中的任何一个时启用“ Devam Et”按钮。听起来很简单,但是关键部分如下...当我单击前8个按钮中的任何一个时,如果要选择后两个按钮,我想禁用它们;如果我选择了后两个选项中的任何一个,我也要禁用所有其他8个选项(如果已选择)。

enter image description here

enter image description here

enter image description here

前3个按钮配置行的代码如下...其余的与这些相同。

vstack{
                        HStack {
                            Button(action: {
                                self.tap1.toggle()
                            }) {
                                ZStack {
                                    Rectangle()
                                        .fill(self.tap1 ? Color(#colorLiteral(red: 0.9254902005,green: 0.2352941185,blue: 0.1019607857,alpha: 1)) : Color(#colorLiteral(red: 0.936548737,green: 0.936548737,blue: 0.936548737,alpha: 1)))
                                        .frame(width: 25,height: 25)
                                    if self.tap1 {
                                        Image(systemName: "checkmark")
                                    }
                                }.padding(.leading,40)
                            }
                            
                            Spacer()
                            
                            Text("Diyabet")
                                .font(.system(size: 20,weight: .regular,design: .rounded))
                                .padding(.trailing,200)
                                Spacer()
                        }.padding(.bottom,10)
                        
                        
                        HStack {
                            Button(action: {
                                self.tap2.toggle()
                               
                                
                            }) {
                               ZStack {
                                   Rectangle()
                                       .fill(self.tap2 ? Color(#colorLiteral(red: 0.9254902005,alpha: 1)))
                                       .frame(width: 25,height: 25)
                                   if self.tap2 {
                                       Image(systemName: "checkmark")
                                   }
                               }.padding(.leading,40)
                            }
                            
                            Spacer()
                            
                            Text("Yüksek Tansiyon")
                                .font(.system(size: 20,130)
                            Spacer()
                        }.padding(.bottom,10)
                    
                        HStack {
                            Button(action: {
                                self.tap3.toggle()
                                
                            }) {
                                ZStack {
                                    Rectangle()
                                        .fill(self.tap3 ? Color(#colorLiteral(red: 0.9254902005,height: 25)
                                    if self.tap3 {
                                        Image(systemName: "checkmark")
                                    }
                                }.padding(.leading,40)
                            }
button1

}

“ Devam Et”按钮的代码如下...

var button1: some View{
      

  return  Button(action: {
        if self.tap1 == true || self.tap2 == true || self.tap3 == true || self.tap4 == true || self.tap5 == true || self.tap6 == true || self.tap7 == true || self.tap8 == true   {
        self.tap11.toggle()
        }
        else if self.tap9 == true {
        self.tap11.toggle()

        }
        else if self.tap10 == true {
        self.tap11.toggle()
        }
        
    }) {
        Text("Devam Et")
        .font(.system(size: 20,design: .rounded))
        .foregroundColor(Color.white)
        .frame(width: 200,height: 30)
        .padding()
        .background(Color(#colorLiteral(red: 0.3101329505,green: 0.193462044,blue: 0.3823927939,alpha: 1)))
        .cornerRadius(40)
        .shadow(color: .gray,radius: 20.0,x: 20,y: 10)
        .padding(.bottom,70)
    }.background(
                NavigationLink(destination: destinationView,isActive: $tap11) {
                    EmptyView()
                }
                .hidden()
            )
        }

    @viewbuilder
    var destinationView: some View {
        if tap1 || tap2 || tap3 || tap4 || tap5 || tap6 || tap7 || tap8 || tap9 || tap10{
            entrance5()
        }
    }

解决方法

TL; DR:结果视频:https://imgur.com/wRx2Ezg

结果输出:https://imgur.com/H8fWwg0

好的,这是一个冗长的答案,在此之前,我将对您以前的一些代码进行评论:

将每个布尔值保留在各自的字段中并不是一个很好的选择,因为您注意到很难跟踪它们。相反,您可以使用某种结构来跟踪您提供的每个选择。

您还需要进行适当的状态跟踪,目前您需要3种状态:

  1. 标准状态:尚未选择任何内容。
  2. 多选状态:除独占选择外,可以有多个选择。
  3. 独家选择状态:在各自的组中只能选择一个。

您还需要将其集成到您的选择中,以确定您在列表中选择的种类。

最后但并非最不重要的一点,应在当前视图之外进行处理,例如在交互器中进行处理。

这是完整的代码,可以在Playgrounds中进行测试:

import SwiftUI
import PlaygroundSupport

enum ContentType {
    case standard
    case exclusive
}

enum ContentState {
    case none
    case multipleChoice
    case exclusiveChoice
}

struct ContentChoice: Identifiable {
    var id: String { title }
    
    let title: String
    let type: ContentType
    var isSelected: Bool = false
    var isDisabled: Bool = false
}

class ContentInteractor: ObservableObject,ContentChoiceViewDelegate {
    @Published var choices: [ContentChoice] = []
    @Published var state: ContentState = .none {
        didSet {
            print("state is now: \(state)")
            
            switch state {
            case .none:
                exclusiveChoices.forEach { choices[$0].isDisabled = false }
                standardChoices.forEach { choices[$0].isDisabled = false }
            case .multipleChoice:
                exclusiveChoices.forEach { choices[$0].isDisabled = true }
            case .exclusiveChoice:
                standardChoices.forEach { choices[$0].isDisabled = true }
            }
        }
    }
    
    private var exclusiveChoices: [Int] {
        choices.indices.filter { choices[$0].type == .exclusive }
    }
    private var standardChoices: [Int] {
        choices.indices.filter { choices[$0].type == .standard }
    }
    
    private var isExclusiveChoiceSelected: Bool {
        choices.filter { $0.type == .standard && $0.isSelected }.count > 0
    }
    private var selectedMultipleChoiceCount: Int {
        choices.filter { $0.type == .standard && $0.isSelected }.count
    }
    
    func didToggleChoice(_ choice: ContentChoice) {
        guard let index = choices.firstIndex(where: { $0.id == choice.id }) else {
            fatalError("No choice found with the given id.")
        }
        
        // This is where the whole algorithm lies.
        switch state {
            // Phase 1:
            // If the user has not made any choice (state == .none),// Enabling a `.standard` choice should lock the `.exclusive` choices.
            // And vice versa.
            case .none:
                choices[index].isSelected.toggle()

                switch choice.type {
                case .standard:
                    state = .multipleChoice
                case .exclusive:
                    state = .exclusiveChoice
                }

            // Phase 2:
            // If the user is in multiple choice state,// They can only select multiple choices. If any of the multiple choice
            // is still selected,it should stay as is.
            // If every choice is deselected,it should return the state to `.none`.
            case .multipleChoice:
                choices[index].isSelected.toggle()

                switch choice.type {
                case .standard:
                    if selectedMultipleChoiceCount == 0 {
                        state = .none
                    }
                case .exclusive:
                    preconditionFailure("Unexpected choice selection.")
                }
            
            // Phase 3:
            // If the user is in a not-answering state,// They can only change it within themselves.
            // If every choice is deselected,it should return the state to `.none`.
            // Also,every exclusive choice is,exclusive.
            // Hence,if one of them is selected,the others should be deselected.
        case .exclusiveChoice:
            switch choice.type {
            case .standard:
                preconditionFailure("Unexpected choice selection.")
            case .exclusive:
                let isSelecting = !choices[index].isSelected
                
                if isSelecting {
                    exclusiveChoices.forEach { choices[$0].isSelected = false }
                } else {
                    state = .none
                }
            }
            
            choices[index].isSelected.toggle()
        }
    }
    
    func didSelectSubmit() {
        print("Current selection:",choices.filter { $0.isSelected }.map { $0.title })
    }
}

protocol ContentChoiceViewDelegate: AnyObject {
    func didToggleChoice(_ choice: ContentChoice)
    func didSelectSubmit()
}

struct ContentChoiceView: View {
    let choice: ContentChoice
    weak var delegate: ContentChoiceViewDelegate!
    
    init(choice: ContentChoice,delegate: ContentChoiceViewDelegate) {
        self.choice = choice
        self.delegate = delegate
    }
    
    var body: some View {
        HStack {
            Button(action: {
                self.delegate.didToggleChoice(self.choice)
            }) {
                ZStack {
                    Rectangle()
                        .fill(self.choice.isSelected ? Color(#colorLiteral(red: 0.9254902005,green: 0.2352941185,blue: 0.1019607857,alpha: 1)) : Color(#colorLiteral(red: 0.936548737,green: 0.936548737,blue: 0.936548737,alpha: 1)))
                        .frame(width: 25,height: 25)
                    if self.choice.isSelected {
                        Image(systemName: "checkmark")
                    }
                }.padding(.leading,40)
            }.disabled(self.choice.isDisabled)
            
            Spacer()
            
            Text(choice.title)
                .font(.system(size: 20,weight: .regular,design: .rounded))
//                .padding(.trailing,200)
                Spacer()
        }.padding(.bottom,10)
    }
}

struct ContentView: View {
    @ObservedObject var interactor: ContentInteractor
    
    var body: some View {
        VStack {
            ForEach(interactor.choices) { choice in
                ContentChoiceView(choice: choice,delegate: self.interactor)
            }
            Spacer()
            submitButton
        }.padding(.vertical,70)
    }
        
    var submitButton: some View {
        Button(action: {
            self.interactor.didSelectSubmit()
        }) {
            Text("Devam Et")
             .font(.system(size: 20,design: .rounded))
             .foregroundColor(Color.white)
             .frame(width: 200,height: 30)
             .padding()
             .background(Color(#colorLiteral(red: 0.3101329505,green: 0.193462044,blue: 0.3823927939,alpha: 1)))
             .cornerRadius(40)
             .shadow(color: .gray,radius: 20.0,x: 20,y: 10)
        }
    }
}

let interactor = ContentInteractor()

["Diyabet","Yuksek Tansiyon","Astim"].forEach { title in
    interactor.choices.append(ContentChoice(title: title,type: .standard))
}
["Soylememeyi Tercih Ederim","Hicbirini Gecirmedim"].forEach { title in
    interactor.choices.append(ContentChoice(title: title,type: .exclusive))
}

let contentView = ContentView(interactor: interactor)

PlaygroundPage.current.setLiveView(contentView)