You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SwiftUI实现UIStackView.Distribution.fillEqually等效效果的问题

在SwiftUI中实现VStack内视图等高(基于最大视图高度)

需求:iOS 13+,创建VStack,使栈内每个视图高度相同,高度等于栈内最大视图所需的最小空间,不使用硬编码高度。

效果对比

  • 期望效果:所有账户卡片高度统一,高度由内容最多的卡片决定
  • 当前效果:卡片高度随各自内容多少变化,高度参差不齐

UIKit中的实现方式

在UIKit中只需创建垂直UIStackView,设置其distribution为.fillEqually,同时将垂直方向的contentCompressionResistancePriority设为.required,contentHuggingPriority保持默认的.defaultLow即可实现该效果。

SwiftUI中的尝试

尝试过将.frame(maxHeight: .infinity)与.fixedSize(horizontal: false, vertical: true)结合使用,但无效;也尝试在AccountView中添加.frame(maxHeight: .infinity)和Spacer,同样没有变化。

当前代码

struct AccountSelectionView: View {
  @ObservedObject private(set) var viewModel: AccountSelectionViewModel
  
  var body: some View {
    VStack(spacing: 12) {
      ForEach(viewModel.model.accounts, id: \.title) { account in
        AccountView(account: account)
          .padding(16)
          .frame(maxHeight: .infinity)
          .overlay(RoundedRectangle(cornerSize: .init(width: 4, height: 4)).stroke(Color.outlineGrey, lineWidth: 1))
      }
    }
    .fixedSize(horizontal: false, vertical: true)
    
    Spacer()
  }
}
struct AccountView: View {
  let account: Account
  
  var body: some View {
    HStack {
      Image(uiImage: account.image)
        .padding(.trailing, 20)
      
      VStack(alignment: .leading) {
        Text(account.title)
          .font(.appFont("Heavy", size: 16))
        Text(account.message)
          .font(.appFont("Roman", size: 14))
      }
      Spacer()
    }
  }
}

解决方案(iOS 13兼容)

通过PreferenceKey收集所有子视图的高度,计算出最大值后统一应用到每个视图:

  1. 定义PreferenceKey用于传递高度值:
struct ViewHeightKey: PreferenceKey {
    static var defaultValue: CGFloat = 0
    
    static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {
        value = max(value, nextValue())
    }
}
  1. 修改AccountSelectionView,收集高度并应用最大高度:
struct AccountSelectionView: View {
    @ObservedObject private(set) var viewModel: AccountSelectionViewModel
    @State private var maxHeight: CGFloat = 0
    
    var body: some View {
        VStack(spacing: 12) {
            ForEach(viewModel.model.accounts, id: \.title) { account in
                AccountView(account: account)
                    .padding(16)
                    .frame(height: maxHeight) // 应用最大高度
                    .overlay(
                        GeometryReader { proxy in
                            Color.clear
                                .preference(key: ViewHeightKey.self, value: proxy.size.height)
                        }
                    )
                    .overlay(RoundedRectangle(cornerSize: .init(width: 4, height: 4)).stroke(Color.outlineGrey, lineWidth: 1))
            }
        }
        .onPreferenceChange(ViewHeightKey.self) { newHeight in
            maxHeight = newHeight
        }
        
        Spacer()
    }
}
  1. 调整AccountView适配高度:
    如果需要让AccountView内的内容垂直对齐,可在外层包裹VStack并添加Spacer控制布局:
struct AccountView: View {
    let account: Account
    
    var body: some View {
        VStack {
            HStack {
                Image(uiImage: account.image)
                    .padding(.trailing, 20)
                
                VStack(alignment: .leading) {
                    Text(account.title)
                        .font(.appFont("Heavy", size: 16))
                    Text(account.message)
                        .font(.appFont("Roman", size: 14))
                }
                Spacer()
            }
            Spacer() // 让内容顶部对齐,若要居中可添加上下Spacer
        }
    }
}

原理:通过GeometryReader获取每个AccountView的高度,利用PreferenceKey将所有高度传递给父视图,父视图取最大值后再设置给每个子视图,从而实现所有视图等高。

内容的提问来源于stack exchange,提问作者Cameron Porter

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 15:17:44