I have a Rectangle() SwiftUI view. I am trying to animate the fill inside it based on a numeric value. For example the height of the rectangle would be totalHeight * ratio. Here is the code:
struct SquareOptionView: View { let title: String @Binding var voteRatio: Double let color: Color let tapAction: ()->() var body: some View { VStack(spacing: 10.0){ Text(title) Text( "\(voteRatio * 100, specifier: "%.2f")%" ) } .padding(25.0) .frame(maxWidth: .infinity, alignment: .bottom) .background{ color .containerRelativeFrame(.vertical, alignment: .bottom) { length, _ in return length * voteRatio } .animation(.bouncy, value: voteRatio) .frame(alignment: .bottom) } .onTapGesture { tapAction() } } } It works well but the animation ends up going to the center rather than top to bottom. As you can see I tried spamming the bottom alignment but the views still animate to the center of their size.
Code snippets:
import Foundation class SquareOptionsContainerViewModel: ObservableObject{ let firstOptionTitle: String let secondOptionTitle: String @Published var firstVoteRatio: Double @Published var secondVoteRatio: Double let firstOptionClickListener: ()->() let secondOptionClickListener: ()->() init(firstOptionTitle: String, secondOptionTitle: String, firstVoteRatio: Double, secondVoteRatio: Double, firstOptionClickListener: @escaping () -> Void, secondOptionClickListener: @escaping () -> Void) { self.firstOptionTitle = firstOptionTitle self.secondOptionTitle = secondOptionTitle self.firstVoteRatio = firstVoteRatio self.secondVoteRatio = secondVoteRatio self.firstOptionClickListener = firstOptionClickListener self.secondOptionClickListener = secondOptionClickListener } } ParentView:
import SwiftUI struct ContentView: View { @ObservedObject var viewModel: SquareOptionsContainerViewModel var body: some View { HStack(spacing: 0.0){ SquareOptionView(title: viewModel.firstOptionTitle, voteRatio: $viewModel.firstVoteRatio, color: .green){ viewModel.firstVoteRatio = random(min: 0.0, max: 1.0) } Rectangle() .frame(width: 2.0) .foregroundColor( .black ) SquareOptionView(title: viewModel.secondOptionTitle, voteRatio: $viewModel.secondVoteRatio, color: .red){ viewModel.secondVoteRatio = random(min: 0.0, max: 1.0) } } .clipShape( RoundedRectangle(cornerSize: .init(width: 10.0, height: 10.0)) ) .overlay( RoundedRectangle(cornerSize: .init(width: 10.0, height: 10.0)) .stroke(lineWidth: 2.0) ) .padding(.vertical, 10.0) } func random(min: Double, max: Double) -> Double { return random * (max - min) + min } var random: Double { return Double(arc4random()) / 0xFFFFFFFF } } Sample Repo: https://github.com/mrikh/test



ObservedObjectincorrectly. IfContentViewis the owner ofSquareOptionsContainerViewModel, it should be a@StateObjectand initialised directly inContentView. IfContentViewis not the owner, there should be a@StateObjectsomewhere else, and that@StateObjectshould be passed toContentView. That said, I find this view model unnecessary in the first place. I'd suggest putting everything inContentViewas@State.