У меня есть вид теста с кнопками на ответ. После того, как пользователь выберет ответ, соответствующая кнопка должна оставаться выделенной. Проблема в том, что я получаю нежелательную анимацию перехода, кажется, что происходит следующее
- Пользователь удерживает кнопку: isPressed = true
- Пользователь нажимает кнопку: isPressed = false
- Действие кнопки запускается: 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
}
Для следующей демонстрации я добавил масштабную анимацию для лучшей видимости проблемы. Первые взаимодействия - это просто нажатие кнопки без ее фактического выбора. Анимация выглядит хорошо. Последнее взаимодействие - выбор кнопки, и появляется эта странная анимация.
