Кнопка SwiftUI: сохранение выделенного состояния без уродливой анимации - PullRequest
0 голосов
/ 09 марта 2020

У меня есть вид теста с кнопками на ответ. После того, как пользователь выберет ответ, соответствующая кнопка должна оставаться выделенной. Проблема в том, что я получаю нежелательную анимацию перехода, кажется, что происходит следующее

  1. Пользователь удерживает кнопку: isPressed = true
  2. Пользователь нажимает кнопку: isPressed = false
  3. Действие кнопки запускается: answerState == .highlighted

Это приводит к анимации, когда кнопка не выделяется на шаге 2 и снова выделяется на шаге 3, что довольно уродливо. У вас есть идеи, как решить эту проблему?

import Foundation
import SwiftUI

struct QuizView: View {
    @State var quizQuestion = QuizManager.shared.generateQuestion()
    @State var selectedAnswer: Insignia? = nil
    @State var isSolutionPresented = false
    @Environment(\.verticalSizeClass) var verticalSizeClass: UserInterfaceSizeClass?
    @Environment(\.horizontalSizeClass) var horizontalSizeClass: UserInterfaceSizeClass?

    var body: some View {
        QuizGrid(self.quizQuestion.elements) { quizElement in
            if quizElement.type == .question {
                    VStack {
                        Text("Group name")
                            .font(.subheadline)
                            .fontWeight(.light)
                            .padding(.horizontal)
                        Text(quizElement.insignia.wrappedName)
                            .font(.title)
//                            .font(.system(size: 30.0, weight: .heavy, design: .default))
                            //.fontWeight(.heavy)
                            //.lineLimit(2)
                            .multilineTextAlignment(.center)
                            .fixedSize(horizontal: false, vertical: true)
                            .padding(.horizontal)
                            .padding(.top, 5)
                    }
                    .frame(maxWidth: .infinity, maxHeight: .infinity)

            } else {
                Button(action: {
                    self.selectedAnswer = quizElement.insignia
                    DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
                        self.presentSolution()
                    }
                }) {
                    Image("Image")
                        .renderingMode(.original)
                }
                .buttonStyle(
                    QuizAnswerButtonStyle(answerState: self.answerState(answer: quizElement.insignia))
                )
                .disabled(self.selectedAnswer != nil)
            }
        }
        .gridStyle(
            (horizontalSizeClass == .compact && verticalSizeClass == .regular) ? QuizGridStyle(columns: 2, rows: 2, questionPosition: .top, relativeQuestionSize: CGSize(width:1, height: 0.3)) : QuizGridStyle(columns: 4, rows: 1, questionPosition: .top, relativeQuestionSize: CGSize(width:1, height: 0.3))
        )
    }

    struct QuizAnswerButtonStyle: ButtonStyle {
        var answerState: QuizAnswerState

        func makeBody(configuration: Self.Configuration) -> some View {
            ZStack(alignment: .init(horizontal: .center, vertical: .center)) {
                Rectangle()
                    .foregroundColor(.white)
                    .colorMultiply(self.fillColor(answerState: (configuration.isPressed || self.answerState == .highlighted) ? .highlighted : self.answerState))
                configuration.label
            }
            .overlay(
                RoundedRectangle(cornerRadius: 10)
                    .strokeBorder(Color("quizItemBorderColor").opacity(0.2), lineWidth: 1)
            )
            .cornerRadius(10)
        }

        func fillColor(answerState: QuizAnswerState) -> Color {
            switch(answerState) {
            case .neutral:
                return Color("quizItemNeutralColor")
            case .highlighted:
                return Color("quizItemHighlightedColor")
            case .correct:
                return Color("quizItemCorrectColor")
            case .incorrect:
                return Color("quizItemIncorrectColor")             
            }
        }
    }

    func answerState(answer: Insignia) -> QuizAnswerState {
        if self.isSolutionPresented {
            if (self.selectedAnswer?.objectID == answer.objectID && answer.objectID == self.quizQuestion.question.objectID) || (self.selectedAnswer?.objectID != answer.objectID && answer.objectID == self.quizQuestion.question.objectID) {
                return .correct
            } else if self.selectedAnswer?.objectID == answer.objectID && answer.objectID != self.quizQuestion.question.objectID {
                return .incorrect
            }else {
                return .neutral
            }
        } else {
            return self.selectedAnswer?.objectID == answer.objectID ? .highlighted : .neutral
        }
    }

    func presentSolution() {
        withAnimation(.easeInOut(duration: 0.15)) {
            self.isSolutionPresented = true
        }
        let delay = self.selectedAnswer?.objectID == self.quizQuestion.question.objectID ? UserDefaults.standard.double(forKey: "QuizDelayAfterCorrectAnswer") : UserDefaults.standard.double(forKey: "QuizDelayAfterIncorrectAnswer")
        DispatchQueue.main.asyncAfter(deadline: .now() + delay) {
            self.loadQuestion()
        }
    }

    func loadQuestion() {
        self.isSolutionPresented = false
        self.selectedAnswer = nil
        self.quizQuestion = QuizManager.shared.generateQuestion()
    }
}

public enum QuizAnswerState {
    case neutral
    case highlighted
    case correct
    case incorrect
}

Для следующей демонстрации я добавил масштабную анимацию для лучшей видимости проблемы. Первые взаимодействия - это просто нажатие кнопки без ее фактического выбора. Анимация выглядит хорошо. Последнее взаимодействие - выбор кнопки, и появляется эта странная анимация.

demo_transition

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...