I'm trying to conditionally render two Forms based on the selection from a segmented picker. It all works fine when there's no animations, but the moment I add them in, the first Form seems to break.
Code for the 1st Form:
struct CalculateView: View {
@Binding var checkAmount: Double
@Binding var numberOfPeople: Int
@Binding var tipPercentage: Int
@Binding var currentMode: DisplayMode
@FocusState var amountIsFocused: Bool
let tipPercentages: [Int]
var currencyType: FloatingPointFormatStyle<Double>.Currency
var grandTotal: Double
var totalPerPerson: Double
var body: some View {
Form{
Section{
TextField("Amount", value: $checkAmount, format:
.currency(code: Locale.current.currencyCode ?? "USD"))
.keyboardType(.decimalPad)
.focused($amountIsFocused)
Picker("Number of people", selection: $numberOfPeople) {
ForEach(1..<100, id: \.self){
Text($0 == 1 ? "\($0) Person" : "\($0) People")
}
}
}
Section{
Picker("Tip Percentage", selection: $tipPercentage){
ForEach(0..<101){
Text($0, format: .percent)
}
}.pickerStyle(.wheel)
} header: {
Text("How much tip do you want to leave ?")
}
Section{
HStack {
Text("Grand Total")
.foregroundColor(.secondary)
.font(.system(size:14, weight: .regular))
.textCase(.uppercase)
Spacer()
Text(grandTotal, format:
currencyType)
.foregroundColor(tipPercentage == 0 ? .red: .blue)
.font(.system(size: 20, weight: .regular))
}.padding(.horizontal, 10)
VStack(alignment: .leading){
Text("Amount per person")
.foregroundColor(.secondary)
.font(.system(size: 15, weight: .semibold))
.padding(EdgeInsets(top: 5, leading: 10, bottom: 0, trailing: 0))
.textCase(.uppercase)
Text(totalPerPerson, format:
.currency(code: Locale.current.currencyCode ?? "USD"))
.frame(maxWidth: .infinity)
.font(.system(size: 55, weight: .thin))
.padding(EdgeInsets(top: 15, leading: 0, bottom: 20, trailing: 0))
.foregroundColor(.green)
}
} header: {
Text("Final Payment")
.foregroundColor(.orange)
}
}
}
}
Code for the 2nd Form:
struct HistoryView: View {
var body: some View {
Form {
Section {
Text("test")
}
}
}
}
Main ContentView code:
enum DisplayMode: String, Equatable, CaseIterable {
case calculate
case history
}
struct ContentView: View {
@State private var checkAmount = 0.0
@State private var numberOfPeople = 2
@State private var tipPercentage = 20
@State private var currentMode: DisplayMode = .calculate
@FocusState private var amountIsFocused: Bool
let tipPercentages = [10, 15, 20, 25, 0]
var currencyType: FloatingPointFormatStyle<Double>.Currency {
return .currency(code: Locale.current.currencyCode ?? "USD")
}
var grandTotal: Double {
let tipValue = checkAmount / 100 * Double(tipPercentage)
let total = checkAmount + tipValue
return total
}
var totalPerPerson: Double {
let peopleCount = Double(numberOfPeople)
let amountPerPerson = grandTotal / peopleCount
return amountPerPerson
}
var navigationTitle: String {
let title = currentMode == .calculate ? "WeSplit" : currentMode.rawValue.capitalized
return title
}
let swapRight: AnyTransition = .asymmetric(
insertion: .move(edge: .trailing),
removal: .move(edge: .leading)
)
let swapLeft: AnyTransition = .asymmetric(
insertion: .move(edge: .leading),
removal: .move(edge: .trailing)
)
var body: some View {
NavigationView{
VStack {
if currentMode == .calculate {
CalculateView(checkAmount: $checkAmount, numberOfPeople: $numberOfPeople, tipPercentage: $tipPercentage, currentMode: $currentMode, amountIsFocused: _amountIsFocused, tipPercentages: tipPercentages, currencyType: currencyType, grandTotal: grandTotal, totalPerPerson: totalPerPerson)
.transition(swapLeft)
} else {
HistoryView()
.transition(swapRight)
}
}
.animation(.easeInOut, value: currentMode)
.navigationTitle(navigationTitle)
.toolbar{
ToolbarItemGroup(placement: .keyboard) {
Button("Done"){
amountIsFocused = false
}
}
ToolbarItemGroup(placement: .principal) {
Picker("Mode", selection: $currentMode) {
ForEach(DisplayMode.allCases, id: \.self){ mode in
Text(mode.rawValue.capitalized)
}
}.pickerStyle(.segmented)
.fixedSize()
}
ToolbarItemGroup(placement: .navigationBarTrailing) {
if currentMode == .calculate {
Button {
print("Save")
} label: {
Text("Save")
}
}
}
}
}
}
}
Here's what it looks like on the simulator:
I can reproduce the issue, this seems to be a bug, the console is showing some constraint warnings.
A workaround could be using
TabView
. A positive side effect is that you won't need custom transitions, they come with it: