SwiftUI矩形填充从下往上动画异常:偏移至中心问题求助
SwiftUI 底部填充动画问题解决
问题描述
使用SwiftUI的颜色视图实现基于数值的内部填充动画,填充高度为totalHeight * ratio。代码功能正常,但动画效果是向中心偏移而非从下往上扩展,多次设置.bottom对齐无效。
原问题代码
SquareOptionView
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() } } }
ViewModel与父视图
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 } } 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 } }
解决方案
问题根源在于containerRelativeFrame的布局逻辑:它生成的视图尺寸变化时,默认以自身中心为基准缩放,而非固定底部对齐。改用GeometryReader获取父容器高度,直接控制颜色视图的高度并固定底部对齐,即可实现从下往上的填充动画。
修改后的SquareOptionView代码
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, maxHeight: .infinity, alignment: .bottom) .background( GeometryReader { geo in color // 设置填充高度为父容器高度 * 比例,底部对齐 .frame(height: geo.size.height * voteRatio, alignment: .bottom) // 确保颜色视图占满父容器宽度,底部对齐 .frame(maxWidth: .infinity, alignment: .bottom) } // 绑定voteRatio触发动画 .animation(.bouncy, value: voteRatio) ) .onTapGesture { tapAction() } } }
效果说明
修改后,颜色视图会以容器底部为起点,随着voteRatio的变化向上扩展高度,动画过程保持底部固定,完全符合从下往上填充的需求。
内容的提问来源于stack exchange,提问作者Rikh
相关产品推荐
相关产品推荐

